Why can't the Java compiler correctly determine inheritance?

The answer comes down to Java does not support lower bounds on parameterized methods, because such a feature is "not useful enough" , see 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 explicit cast to R in TypicalJavaFooBar#foobar compiler fails with the following error

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

My question is why? It seems to me that the compiler should have enough information, since TypicalJavaFooBar is clearly defined to implement both Foo and Bar ; why is this not enough to satisfy the Foo & Bar restriction?

UPDATE

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

+5
source share
4 answers

A parameter of type R bound to the method by the calling code and theoretically could 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 trying, you need to transfer the type parameter to the FooBar interface and have TypicalJavaFooBar implements Foo, Bar, FooBar<TypicalJavaFooBar> .

+6
source

Your R can be any type that is Foo & Bar , so you can write

  class MyFooBar implements Foo, Bar { ... FooBar x = new TypicalJavaFooBar(); MyFooBar mfb = x.foobar(); // this compiles now with the cast. 
+1
source

No, the compiler cannot conclude that the type R is not suitable. To understand why, simply extend your class as follows:

 static class C extends TypicalJavaFooBar { void eat() {} @Override public void bar() {} @Override public void foo() {} } 

Then your roll allows you to:

 FooBar x = new TypicalJavaFooBar(); C c = x.foobar(); c.eat(); 

As a result, a RuntimeException is thrown:

 Exception in thread "main" java.lang.ClassCastException: demo.Demo$TypicalJavaFooBar cannot be cast to demo.Demo$C 

That's why - for sure, the compiler designers did not allow this. There is something wrong with the code, so a necessity .

0
source

If you can use this in Java

 if (obj instanceof (Foo & Bar)) 

then you will not be forced to throw.

0
source

Source: https://habr.com/ru/post/1241544/


All Articles