Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overriding a method as abstract #9998

Closed
straight-shoota opened this issue Nov 30, 2020 · 5 comments · Fixed by #11056
Closed

Overriding a method as abstract #9998

straight-shoota opened this issue Nov 30, 2020 · 5 comments · Fixed by #11056

Comments

@straight-shoota
Copy link
Member

You can define a method as abstract that is already defined by a supertype. The following code compiles:

class Foo
  def foo; end
end

abstract class Bar < Foo
  abstract def foo
end

class Baz < Bar; end

I don't think that should compile. The intention of the abstract method is to force subtypes to implement it. When it's already defined in the supertype, that defeats the purpose.
So it would be better to make overriding a method as abstract an error to show the programmer this doesn't work as intended.
Or even better, actually override the method as abstract and force subtypes to implement it.

In Java for example, this works:

class Foo {
    void foo() { }
}

abstract class Bar extends Foo {
    abstract void foo();
}

class Baz extends Bar { } // Error: Baz is not abstract and does not override abstract method foo() in Bar
@straight-shoota
Copy link
Member Author

When the abstract method has a stricter return type than the supertype's method:

class Foo
  def foo; end # Error: this method overrides Bar#foo() which has an explicit return type of Nil.
end

abstract class Bar < Foo
  abstract def foo : Nil
end

class Baz < Bar; end

Compared to the first example, only the return type Nil is added to abstract Bar#foo. But an error is shown at Foo#foo, which really doesn't override Bar#foo. It's Bar that inherits this definition.

@mattrberry
Copy link
Contributor

Thanks for creating an issue for this! I'm fully on board with Java's semantics here.

Regarding your follow-up comment, I have another question. Is there a reason that methods like Object#=== don't just define a return type of Bool?

@HertzDevil
Copy link
Contributor

Interesting fact, this already happens to Indexable#size (which is a module including another module, but the same principles apply):

class Foo
  include Indexable(Nil)
  
  def unsafe_fetch(index : Int)
  end
end

Foo.new.size # Stack overflow (e.g., infinite or very deep recursion)

@HertzDevil
Copy link
Contributor

HertzDevil commented Jul 21, 2021

For the record I verified that overriding from a sibling type works in Java only if implementation comes from a class and the visibility matches:

class Foo {
    public void foo() { }
}

interface Bar {
    void foo();
}

class Baz extends Foo implements Bar { } // okay
abstract class Foo {
    abstract public void foo();
}

interface Bar {
    default void foo() { }
}

class Baz extends Foo implements Bar { } // error: Baz is not abstract and does not override abstract method foo() in Foo
interface Foo {
    void foo();
}

interface Bar {
    default void foo() { }
}

class Baz implements Foo, Bar { } // error: Baz is not abstract and does not override abstract method foo() in Foo

@straight-shoota
Copy link
Member Author

Implementation in #11056.

I'm not sure if we should change this directly to an error or start with a deprecation first. I'd consider code that inherits an abstract def implementation from a supertype broken. But it used to work until now. And it can easily happen if you inherit stdlib's Indexable without defining a #size method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants