Lets start with 3 interfaces. What they do isn't important. Just note that Car is parameterized (), while Foo and Bar are not.
interface Foo {void testFoo();}
interface Bar {void testBar();}
interface Car<A> {A testCar();}
I want to 'composite' these interfaces, and this works just fine if I explicitly create the composites like this:
interface FooBar extends Foo,Bar {}
interface FooCar<A> extends Foo,Car<A> {}
However, I'd much prefer to implicitly composite the interfaces via the bounded type declarations of various methods. For example:
public <T extends Foo & Bar> T implicitFooBar() {return null;}
public <X, T extends Foo & Car<X>> T implicitFooCar() {return null;}
WORKs: The implictFooBar() method returns a type T which implements both the Foo and Bar interfaces (a composite if you will). A call to this method compiles, and I can avoid having to explicitly declare the FooBar interface:
// implicit composition of Foo and Bar, GOOD
FooBar implicitFooBar = implicitFooBar();
implicitFooBar.testFoo();
implicitFooBar.testBar();
FAILs: However, a call to implicitFooCar() fails to compile. The error message is "The method implicitFooCar() in the type GenericsTest is not applicable for the arguments ()" (I wrapped my test code in a class called GenericsTest.)
// implicit composition of Foo and Car<X>, FAIL!
//Compiler says "The method implicitFooCar() in the type GenericsTest is not applicable for the arguments ()"
FooCar<Number> implicitFooCar = implicitFooCar(); //compile error on method call
implicitFooCar.testFoo();
Number n2 = implicitFooCar.testCar();
The compiler error only shows when the type declaration is both compound and parameterized. For example, both of these compile and can be called just fine:
public <X> Car<X> justCar() {return null;}
public <X, T extends Car<X>> T implicitCar() {return null;}
I suspect this has something to do with type erasure, but I'd like to understand the details of what's going on here. I've read the Oracle Generics Tutorials, but I'm not seeing what combination of rules the implicitFooCar() method violates, while implicitFooBar() and implicitCar() are fine. I'm looking for an academic style explanation, not just a work around.
Interestingly enough, the following variant of calling the implicitFooCar() method works (no compiler errors.) This hints at why the other version doesn't work, but I've yet to connect those dots.
//variant... GOOD... but why?
implicitFooCar = this.<Number,FooCar<Number>>implicitFooCar();
If you want to play with the code, here it is as a single class.
public class GenericsTest {
public static interface Foo {void testFoo();}
public static interface Bar {void testBar();}
public static interface Car<A> {A testCar();}
public static interface FooBar extends Foo,Bar {}
public static interface FooCar<A> extends Foo,Car<A> {}
public <X> Car<X> justCar() {return null;}
public FooBar explicitFooBar() {return null;}
public <T extends Foo & Bar> T implicitFooBar() {return null;}
public <X> FooCar<X> explicitFooCar() {return null;}
public <X, T extends Foo & Car<X>> T implicitFooCar() {return null;}
public <X, T extends Car<X>> T implicitCar() {return null;}
public void test() {
justCar().testCar();
// explicit composition of Foo and Bar, GOOD
FooBar explicitFooBar = explicitFooBar();
explicitFooBar.testFoo();
explicitFooBar.testBar();
// explicit composition of Foo and Car<X>, GOOD
FooCar<Number> explicitFooCar = explicitFooCar();
explicitFooCar.testFoo();
Number n1 = explicitFooCar.testCar();
// implicit composition of Foo and Bar, GOOD
FooBar implicitFooBar = implicitFooBar();
implicitFooBar.testFoo();
implicitFooBar.testBar();
// implicit composition of Foo and Car<X>, FAIL!
//Compiler says "The method implicitFooCar() in the type GenericsTest is not applicable for the arguments ()"
FooCar<Number> implicitFooCar = implicitFooCar(); //compile error on method call
implicitFooCar.testFoo();
Number n2 = implicitFooCar.testCar();
//variant... GOOD... but why?
implicitFooCar = this.<Number,FooCar<Number>>implicitFooCar();
// implicit (no composition) Car<X>, GOOD
Car<Number> implicitCar = implicitCar();
Number n3 = implicitCar.testCar();
}
}
This compiles with javac version 1.8.0_60 (and _45 per comments), but Eclipse's (version 4.4.2.M20150204-1700) built in compiler ECJ is reporting the error mentioned above. I added an eclipse tag to this question, as this may be an EJC issue.
First, to directly answer my question... It IS possible to declare a method with a bound parameterized type that combines two interfaces, one of which is parameterized itself.
The implicitFooCar() method from my example failed because of a bug in the Eclipse compiler ECJ used in Luna, version 4.4 of Eclipse. The same code compiled using the javac compiler (v 1.8.0_60) and in the next version of Eclipse, Mars (4.5).
Second, my desire to avoid explicit declarations of what I thought of as temporary or intermediary composite interfaces, was short sighted. Louis Wasserman pointed out that the method will at some point need to return an object that conforms to that composite specification, and thus I'll need an explicit version at that point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With