通用方法中的绑定类型参数在等效通用接口工作时失败,为什么?

时间:2015-11-12 23:52:08

标签: java eclipse generics

让我们从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问题。

1 个答案:

答案 0 :(得分:2)

首先,直接回答我的问题......可以声明一个带有绑定参数化类型的方法,该方法结合了两个接口,其中一个接口本身是参数化的。

我的示例中的 implicitFooCar()方法失败了,因为Eclipse中使用的Eclipse编译器ECJ中存在一个错误。使用javac编译器(v 1.8.0_60)和Eclipse的下一版本Mars(4.5)编译相同的代码。

其次,我希望避免明确声明我所认为的临时或中间复合接口,这是短视的。 Louis Wasserman pointed out该方法在某些时候需要返回一个符合该复合规范的对象,因此我在那时需要一个显式版本。