4
public class GenericClass<T> {

    class MyClass {
    }

    public GenericClass(final T[] param) {
        MyClass myObject = new MyClass();                       // OK
        MyClass[] myArray = { new MyClass(), new MyClass() };   // Cannot create a generic array of GenericClass<T>.MyClass
    }
}

这不是创建通用数组。编译器理解/确定应该没有问题MyClass,不是吗?

4

6 回答 6

2

内部类“知道”封闭类的哪个实例创建了它们,并且可以访问该实例的字段/成员。就好像它们有第二个this变量,其类型是封闭类的具体类型(例如GenericClass<String>)。

为了克服这个困境,你可以做出MyClass static。这将使它与封闭类的任何实例完全解耦(即:它不会有那个 second this),因此它们可以被自由地实例化:

public class GenericClass<T> {

  static class MyClass {
  }

  public GenericClass(final T[] param) {
    MyClass myObject = new MyClass();                       // OK
    MyClass[] myArray = { new MyClass(), new MyClass() };   
  }
}
于 2013-07-20T06:29:06.380 回答
1
{ new MyClass(), new MyClass() }; //new MyClass() => new GenericClass<T>.MyClass()

上面的代码将被视为对象数组,因为 T 是未知的,由于泛型的实现方式(通过擦除),数组的类型没有明确定义。一方面,它应该是 MyClass 的数组,另一方面,它应该是 Object 的数组

创建对象类型数组并将其转换为您的类型

Object[] arr=new Object[]{this.new MyClass(), this.new MyClass()};
MyClass[]  myArray = Arrays.copyOf(arr,arr.length, Item.MyClass[].class);   

如果将其设为静态,它将起作用,因为 - 除了命名空间嵌套和对私有变量的访问之外,静态嵌套类或嵌套接口(顺便说一下,它始终是静态的)与其外部类(或接口)没有任何关系。作为标准 API 中的示例,查找接口 Map.Entry,它嵌套在接口 Map 中,但无法访问其类型参数,需要再次声明它们。

于 2013-07-20T06:24:42.957 回答
1

这里有一些额外的信息。从链接...

Java 数组携带运行时类型信息,用于标识所包含元素的类型

对于编译器,您的代码如下所示:

MyClass[] myArray = {new GenericClass<T>.MyClass(), ..} //T is unknown
于 2013-07-20T05:40:16.617 回答
1

涵盖此内容的 JLS 部分是10.6。具体来说,这是因为:

如果 ClassOrInterfaceType 不表示可具体化类型(第 4.7 节),则为编译时错误。否则,ClassOrInterfaceType 可以命名任何命名的引用类型,甚至是抽象类类型(§8.1.1.1)或接口类型(§9)。

上面的规则意味着数组创建表达式中的元素类型不能是参数化类型,除了无界通配符。

因为MyClass它是非静态的,所以它依赖于外部类;它实际上GenericClass<T>.MyClass是参数化类型。声明它static会消除这种依赖并解决问题。

如果你这样做,就会变得奇怪。

class MyClass<T> {
}

public GenericClass(final T[] param) {
    MyClass[] myArray = { new MyClass(), new MyClass() };  
}

这是合法的。古怪,有点笨拙,但合法。因为您重新声明了类型,所以它隐藏了外部类型。然后......数组和泛型不要混合......除非你使用原始类型。为了向后兼容,您可以拥有一个原始类型数组,该数组最终包含MyClass<Object>. 这是一件非常糟糕的事情,但它确实可以编译。你可以在这里摆脱创造性的演员阵容,但最后......只是......不要。

于 2013-07-20T06:48:03.880 回答
0

@ItayMaman 有正确的理由。基本上,MyClass不是具体化的类型。

MyClass是一个非静态内部类。由于它是非静态的,因此它在其封闭类的类型参数范围内。而且每次自己写MyClass在一个实例方法中GenericClass,其实都是简写GenericClass<T>.MyClass。所以即使看起来不像,MyClass(本身)实际上是一个参数化类型(由 参数化T),类似于List<String>. 所以当你这样做时new MyClass[2],你正在尝试创建一个参数化类型的数组,就像new List<String>[2]. 我想你已经知道这是不允许的。

你该怎么办?这一切都取决于你的意图是什么。人们建议的一件事是制作MyClass静态的。当然,这会超出T. 但这可能是也可能不是您想要的,因为它完全改变了它与GenericClass. 非静态内部类可以访问封闭类的实例,这也许就是您一开始这样做的原因。如果您从未打算让它成为非静态的(并且错误地做到了),那么这显然是要走的路。

如果您想要一个非静态内部类,并且您只是想创建一个这种类型的数组,那么让我们考虑一下您通常如何处理参数化类型的数组,例如List<String>[].

  • 一种解决方案是创建一个原始类型的数组,例如List[] foo = new List[2];. 对于我们的案例,等效的方法是GenericClass.MyClass[] foo = new GenericClass.MyClass[2];. 注意我们在这里做了什么。为了编写原始类型,我们必须MyClass使用未参数化的外部类名显式限定。如果我们没有明确地限定它,那么它就会被隐含地限定为GenericClass<T>,如上所述,这不是我们想要的。将其转换为示例中的代码,您将编写GenericClass.MyClass[] myArray = { new MyClass(), new MyClass() };

  • 同样,如果我们想避免原始类型,我们可以创建一个通配符类型的数组,例如List<?>[] foo = new List<?>[2];. 对于我们的案例,等效的方法是GenericClass<?>.MyClass[] foo = new GenericClass<?>.MyClass[2];. 因此,将其转换为示例中的代码,您将编写GenericClass<?>.MyClass[] myArray = { new MyClass(), new MyClass() };

  • 最后,我们可能希望创建一个通配符类型的数组,然后再转换回参数化类型的数组,以便以后使用。例如List<String>[] foo = (List<String>[])new List<?>[2];。对于我们的案例,等效的方法是MyClass[] myArray = (MyClass[])new GenericClass<?>.MyClass[] { new MyClass(), new MyClass() };. 请注意,演员表是未经检查的演员表。这样做的好处是,当您从 中取出内容时myArray,它将是 type MyClass,而不是上述方法中的原始类型GenericClass.MyClass或通配符类型GenericClass<?>.MyClass

于 2013-07-22T08:08:17.190 回答
0

这里的问题是编译器无法在编译时确定数组myArray的信息。它被认为是通用的,因为(正如 eclipse 向您展示的那样)它在 {new GenericClass<T>.MyClass(), ...} 中转换。这是因为您将类 MyClass 放在泛型类中。

此代码也不起作用:

package my.stuff;

public class GenericClass<T> {

    class MyClass {
        static MyClass[] myArray = { new MyClass(), new MyClass() };;
    }

    public GenericClass(final T[] param) {
        MyClass myObject = new MyClass();
    }
}

但这段代码有效:

package my.stuff;

public class GenericClass<T> {
    public GenericClass(final T[] param) {
        MyClass myObject = new MyClass();
        MyClass[] myArray = { new MyClass(), new MyClass() };
    }
}

class MyClass {
}

因为您没有在 MyClass 中使用泛型,所以最好的办法可能是第二个。

如果您将其声明为静态,则编译器知道 MyClass 不是通用的并且它知道要做什么。

此外,在 java 中创建泛型数组的唯一方法是创建一个原始类型,然后将其转换为泛型(参见此处:“无法创建 .. 的泛型数组” - 如何创建 Map<String, Object> 的数组?)。因此,如果您在泛型中绝对需要 myClass,则应将其转入 MyClass<T>,然后使用技巧:创建一个原始类型并将其强制转换为 MyClass<T>:

package my.stuff;

public class GenericClass<T> {

    class MyClass<T> {
    }

    @SuppressWarnings("unchecked")
    public GenericClass(final T[] param) {
        MyClass<T> myObject = new MyClass<T>();
        MyClass<T>[] myArray = new MyClass[]{ new MyClass<T>(), new MyClass<T>() };
    }
}

即使您不在 MyClass 类中使用 T。

于 2013-07-20T06:51:34.057 回答