Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't the Java compiler properly infer inheritance?

Tags:

java

generics

Answer boils down to Java does not support lower bounds on parameterized methods, because such a feature is "not useful enough", refer to a similar question

Given the following snippet:

package demo;

public class Demo {
    interface Foo { void foo(); }
    interface Bar { void bar(); }
    interface FooBar {
      <R extends Foo & Bar> R foobar();

      static FooBar create() { return new TypicalJavaFooBar(); }
    }

    private static final class TypicalJavaFooBar implements Foo, Bar, FooBar {
        public void bar() { System.out.println("foo"); }
        public void foo() { System.out.println("bar"); }

        public <R extends Foo & Bar> R foobar() {
            return (R) this;
        }
    }

    public static void main(String[] args) {
        FooBar x = FooBar.create();
        Foo foo = x.foobar();
        Bar bar = x.foobar();
        x.foobar().foo();
        x.foobar().bar();
    }
}

Without the explicit cast to R in TypicalJavaFooBar#foobar compiler fails with the following error

Error:(13, 20) java: incompatible types: demo.Demo.TypicalJavaFooBar cannot be converted to R

My question is why? To me, it seems that the compiler should have enough info since TypicalJavaFooBar is clearly defined to implement both Foo and Bar; why isn't that enough to satisfy the Foo & Bar constraint?

UPDATE

The main goal of this exercise is to define the following contract: calling method foobar on an instance of a FooBar is guaranteed to return something that implements both Foo and Bar.

like image 629
Andrey Avatar asked Jan 26 '16 19:01

Andrey


1 Answers

The type parameter R is bound to the method by the calling code and could theoretically be Baz implements Foo, Bar; see, for example, Collections.emptySet(), whose type parameter is determined by the caller and can be influenced by a type witness.

To do what you are apparently attempting, you would need to move the type parameter onto the interface FooBar and have TypicalJavaFooBar implements Foo, Bar, FooBar<TypicalJavaFooBar>.

like image 82
chrylis -cautiouslyoptimistic- Avatar answered Oct 02 '22 05:10

chrylis -cautiouslyoptimistic-