让我们从3个接口开始。他们所做的并不重要。请注意,Car是参数化(),而Foo和Bar不是。
interface Foo {void testFoo();}
interface Bar {void testBar();}
interface Car<A> {A testCar();}
我想'复合'这些接口,如果我明确地创建这样的复合材料,这可以正常工作:
interface FooBar extends Foo,Bar {}
interface FooCar<A> extends Foo,Car<A> {}
但是,我更喜欢通过各种方法的有界类型声明隐式组合接口。例如:
public <T extends Foo & Bar> T implicitFooBar() {return null;}
public <X, T extends Foo & Car<X>> T implicitFooCar() {return null;}
工作: implictFooBar()方法返回一个类型T,它实现了Foo和Bar接口(如果你愿意,可以复合)。对此方法的调用进行编译,我可以避免必须显式声明FooBar接口:
// implicit composition of Foo and Bar, GOOD
FooBar implicitFooBar = implicitFooBar();
implicitFooBar.testFoo();
implicitFooBar.testBar();
失败:但是,对 implicitFooCar()的调用无法编译。错误消息是“GenericsTest类型中的方法implicitFooCar()不适用于arguments()”(我将我的测试代码包装在一个名为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();
编译器错误仅在类型声明同时为复合和参数化时显示。例如,这两个编译都可以调用就好了:
public <X> Car<X> justCar() {return null;}
public <X, T extends Car<X>> T implicitCar() {return null;}
我怀疑这与类型擦除有关,但我想了解这里发生了什么的细节。我已经阅读了Oracle泛型教程,但我没有看到implicitFooCar()方法违反了哪些规则组合,而implicitFooBar()和implicitCar()都没问题。我正在寻找一种学术风格的解释,而不仅仅是一种解决方法。
有趣的是,调用implicitFooCar()方法的以下变体有效(没有编译器错误。)这暗示了为什么其他版本不起作用,但我还没有连接这些点。
//variant... GOOD... but why?
implicitFooCar = this.<Number,FooCar<Number>>implicitFooCar();
如果你想玩代码,这里就是一个单独的类。
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();
}
}
这与javac版本1.8.0_60(和每个注释_45)一起编译,但是在编译器ECJ中内置的Eclipse(版本4.4.2.M20150204-1700)报告了上述错误。 我在这个问题上添加了一个eclipse标签,因为这可能是一个EJC问题。
答案 0 :(得分:2)
首先,直接回答我的问题......可以声明一个带有绑定参数化类型的方法,该方法结合了两个接口,其中一个接口本身是参数化的。
我的示例中的 implicitFooCar()方法失败了,因为Eclipse中使用的Eclipse编译器ECJ中存在一个错误。使用javac编译器(v 1.8.0_60)和Eclipse的下一版本Mars(4.5)编译相同的代码。
其次,我希望避免明确声明我所认为的临时或中间复合接口,这是短视的。 Louis Wasserman pointed out该方法在某些时候需要返回一个符合该复合规范的对象,因此我在那时需要一个显式版本。