5

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.

Community
  • 1
  • 1
Andrey
  • 8,882
  • 10
  • 58
  • 82
  • @horatius That has nothing to do with this question. – chrylis -cautiouslyoptimistic- Jan 26 '16 at 19:20
  • In `TypicalJavaFooBar` you can change the return type of the `foobar` method to just `TypicalJavaFooBar`, which also does away with the cast. While this does not answer your question, this is what I would probably do in this situation. Though I generally prefer composition above inheritance and thus am unlikely to encounter this situation. :-) – Waldheinz Jan 26 '16 at 19:29
  • @Andrey, unfortunately your "main goal" is not actually possible in Java's type system. You can return something that is guaranteed to implement a specific interface, but you can't express "something that implements both of these interfaces." – Louis Wasserman Jan 26 '16 at 20:38
  • @Louis Wasserman, yep you're right, I just tried making "Foo & Bar" a lower bound, but apparently "" isn't valid – Andrey Jan 26 '16 at 21:51
  • Possible duplicate of [Why can't a Java type parameter have a lower bound?](http://stackoverflow.com/questions/4902723/why-cant-a-java-type-parameter-have-a-lower-bound) – Andrey Jan 26 '16 at 21:56

4 Answers4

6

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>.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
1

Your R can be any type which is a 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.
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

No, the compiler cannot inference the type of R and your cast is wrong. To see why, just extend your class like this:

static class C extends TypicalJavaFooBar {

    void eat() {}

    @Override
    public void bar() {}

    @Override
    public void foo() {}
}

Then, your cast allows this:

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

Which results in a RuntimeException:

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

That is why -- surely the designers of the compiler would not allow this. There is something wrong in the code, hence the necessity of the cast.

smarquis
  • 520
  • 2
  • 7
  • I updated the answer, i was just trying to keep the demo short... honestly, I find non-final, public classes or final classes with public constructors to be a horrendous practice ever since Java 8 came out. also, you're missing the point. the point here is to make the following contract explicit: calling method "foobar" on an instance of a "FooBar" is guaranteed to return *something* that implements both "Foo" and "Bar". – Andrey Jan 26 '16 at 20:18
  • 1
    If you find non-final, public classes horrendous, you will not like other features of the language, like the necessity of cast to show the risk of ClassCastException at runtime. – smarquis Jan 26 '16 at 20:28
  • static factory methods in interfaces = not dealing with public classes. also, what *would* make it work is defining Foo & Bar as a *lower* bound, i.e.: "", i see no valid reason why specifying lower bounds is not supported... – Andrey Jan 26 '16 at 21:48
0

If you could use this in Java

if (obj instanceof (Foo & Bar))

then you would not be forced to make the cast.

Radu Ionescu
  • 3,462
  • 5
  • 24
  • 43
  • The necessity of the cast is here to show the risk of java.lang.ClassCastException at runtime. – smarquis Jan 26 '16 at 20:27
  • 1
    It could be..IDK...but to me there is no logical difference between `if (obj instanceof (Foo & Bar))` and `if ((obj instanceof Foo ) & (obj instanceof Bar))`, but the first does not compile however – Radu Ionescu Jan 26 '16 at 21:43