2

为什么在实例化通用外部类(与内部类一起)时使用菱形运算符会在片段 2 中产生错误,而片段 1 则完全没问题?

我知道稀有类型是被禁止的,但我的情况不是稀有类型——在稀有类型中,外部和内部都是通用的,但其中一个(任何一个)用作原始类型,另一个用作通用类型。

片段1:

class Outer {
    class Inner<T> {

    }
}

class Driver {
    public static void main(String[] args) {

        Outer.Inner obj1 = new Outer().new Inner<>(); // fine !
        Outer.Inner<String> obj2 = new Outer().new Inner<>(); // fine !

    }
}

片段 2:

class Outer<T> {
    class Inner {

    }    
}

class Driver {    
    public static void main(String[] args) {

        Outer.Inner obj1 = new Outer<>().new Inner();  // fine !
        Outer<String>.Inner obj2 = new Outer<>().new Inner(); // error !

    }
}

PS 在 Eclipse 编译器上测试。

4

3 回答 3

2

您正在尝试使用菱形运算符来推断类型参数:

Outer<String>.Inner obj2 = new Outer<>().new Inner(); // error !

JLS 第 15.9 节对钻石这样说:

类实例创建表达式指定要实例化的类,如果要实例化的类是通用的,则可能后跟类型参数(§4.5.1)或菱形(<>)

您有两个类实例化表达式:

new Outer<>()

new Inner()  // or more precisely, new Outer<>().new Inner()

在第 15.9 节的末尾,它区分了这两种表达方式:

如果类实例创建表达式使用菱形形式作为类的类型参数,并且它出现在赋值上下文或调用上下文中(第 5.2 节、第 5.3 节),则它是一个多边形表达式(第 15.2 节)。否则,它是一个独立的表达式。

因此,第二个表达式是一个 poly 表达式,这意味着它的类型取决于赋值上下文;但第一个表达式 ,new Outer<>()是一个独立的表达式:它的类型不受任何上下文的影响。

你的另一个陈述

Outer.Inner obj2 = new Outer<>().new Inner();

很好,因为您使用Outer的是原始类型。

于 2017-10-26T09:13:44.070 回答
1

让代码编译的解决方案包含在 alfasin 的答案中。

但是,原因在于 Java 是如何推断类型的。Java 只能在某些情况下推断类型(请参阅此处的文档以获取更多详细信息,特别是 部分Target Types)。

引用这些文档:

注意:重要的是要注意推理算法仅使用调用参数、目标类型和可能的明显预期返回类型来推断类型。推理算法不使用程序后面的结果。

您的代码片段的问题归结为目标类型推断所推断的类型以及使用点运算符链接方法。使用点运算符时,第一种方法的结果首先用于第二种方法(通过管道传输)。因此,这会更改目标类型,使其成为点后部分所期望的类型。但是,只有方法链中最后一个方法的结果被分配给变量类型,因此可以推断出您案例中最后一个方法的泛型类型。

所以在第一个片段中,需要推断类型的部分是点运算符之后的最后一个方法,因此该方法的目标类型是要分配结果的变量,因此可以推断。即从你的第一个片段中:

Outer.Inner<String> obj2 = new Outer().new Inner<>(); // fine !

由于“new Outer()”返回一个没有泛型类型的对象,因此不需要进行推理。第二种方法new Inner<>()可以继续。此处方法的new Inner<>()结果将分配给obj2声明为具有 type的变量Outer.Inner<String>,因此可以从它的目标推断为Tis String

在第二个片段中,需要推断类型的部分是点运算符之前的方法,这意味着第一种方法的结果就是第二种方法所应用的结果。

因此,从您的第二个片段中,该行:

Outer<String>.Inner obj2 = new Outer<>().new Inner();

的目标类型new Outer<>()new Inner()预期的,即Outer<T>.newInner(); 由于类型擦除和 Java 泛型的工作原理,这个 T,因为它没有被指定为某些特定类型,所以被视为Object. 现在第二部分可以继续,但是第二个方法的结果现在最终是 type Outer<Object>.Inner,它不能转换为变量的分配类型。

因此,您需要为该方法提供类型见证,即new Outer<SomeMethod>(),因为它在第二个片段中没有正确的目标类型,无法以您想要的方式进行类型推断。

于 2017-10-26T10:31:25.953 回答
0

编译错误:

错误:(11, 50) java: 不兼容的类型:Outer< java.lang.Object>.Inner 无法转换为 Outer< java.lang.String>.Inner

这意味着您应该更改:

Outer<String>.Inner obj2 = new Outer<>().new Inner()

至:

Outer<String>.Inner obj2 = new Outer<String>().new Inner()

编译器new Outer<>()在期望时将其视为原始类型(默认为 Object)Outer<String>,因此要解决它,我们需要更具体地使用我们在分配中提供的泛型类型。

于 2017-10-26T09:03:00.897 回答