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
时应该没有问题,不是吗?
答案 0 :(得分:2)
内部类“知道”封闭类的哪个实例创建它们,并且可以访问此实例的字段/成员。就好像它们有第二个this
变量,其类型是封闭类的具体类型(例如GenericClass<String>
)。
要克服这种困境,您可以MyClass
static
。这将使它完全与封闭类的任何实例分离(即:它不会有第二个this
),因此它们可以自由地实例化:
public class GenericClass<T> {
static class MyClass {
}
public GenericClass(final T[] param) {
MyClass myObject = new MyClass(); // OK
MyClass[] myArray = { new MyClass(), new MyClass() };
}
}
答案 1 :(得分:1)
这里有一些additional information。从链接...
Java数组携带标识其类型的运行时类型信息 包含的元素
编译器的代码如下所示:
MyClass[] myArray = {new GenericClass<T>.MyClass(), ..} //T is unknown
答案 2 :(得分: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接口内的接口Map.Entry,但是无法访问其类型参数,需要再次声明它们。
答案 3 :(得分: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>
的rawtype数组。这是一个非常糟糕的事情,但它确实编译。你可以在这里摆脱创意铸造,但最终......只是......不要。
答案 4 :(得分:0)
这里的问题是编译器无法在编译时确定数组myArray的信息。它被认为是通用的,因为(正如eclipse所示)它在{new GenericClass&lt; T&gt; .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中创建泛型数组的唯一方法是创建一个原始类型,然后将其强制转换为泛型(参见此处:"Cannot create generic array of .." - how to create an Array of Map<String, Object>?)。因此,如果你绝对需要在通用的myClass中使用myClass,你应该在MyClass&lt; T&gt;中使用它,然后使用技巧:创建一个原始类型并将其转换为MyClass&lt; T&gt;:
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。
答案 5 :(得分: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];
。注意我们在这里做了什么。为了编写原始类型,我们必须使用 unparameterized 外部类名明确限定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
中获取信息时,它将是MyClass
类型,而不是上述方法中的原始类型GenericClass.MyClass
或通配类型GenericClass<?>.MyClass
。