[英]Why can't we create an array of “Concrete” class inside a generic class?
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
}
}
This is not creating a generic array. 这不是创建通用数组。 The compiler should have no problems understanding/determining
MyClass
, isn't it? 编译器应该没有理解/确定
MyClass
问题,不是吗?
Inner classes "know" which instance of the enclosing class created them, and can access fields/members of this instance. 内部类“知道”封闭类的哪个实例创建它们,并且可以访问该实例的字段/成员。 It is as if they have a second
this
variable whose type is the concrete type of the enclosing class (such as GenericClass<String>
). 就好像它们有第二个
this
变量,其类型是封闭类的具体类型(例如GenericClass<String>
)。
To overcome this predicament you can make MyClass
static
. 为了克服这种困境,你可以使
MyClass
static
。 This will make it completely decoupled of any instance of the enclosing class (that is: it will not have that second this
) so they can be instantiated freely: 这将使它完全解耦封闭类的任何实例(即:它不会有第二
this
),使他们能够自由地进行实例化:
public class GenericClass<T> {
static class MyClass {
}
public GenericClass(final T[] param) {
MyClass myObject = new MyClass(); // OK
MyClass[] myArray = { new MyClass(), new MyClass() };
}
}
Here's some additional information . 这是一些额外的信息 。 From the link ...
从链接......
Java arrays carry runtime type information that identifies the type of the elements contained
Java数组携带运行时类型信息,用于标识所包含元素的类型
to the compiler your code looks like this: 对于编译器,您的代码如下所示:
MyClass[] myArray = {new GenericClass<T>.MyClass(), ..} //T is unknown
{ new MyClass(), new MyClass() }; //new MyClass() => new GenericClass<T>.MyClass()
Above code will be treated as array of object as T is unknown ,due to the way generics are implemented (by erasure), the type of the array is not well-defined . 上面的代码将被视为对象数组,因为T是未知的,由于实现泛型的方式(通过擦除),数组的类型没有明确定义 。 On one hand, it should be an array of MyClass , on the other hand, it should be an array of Object
一方面,它应该是一个MyClass数组,另一方面,它应该是一个Object数组
Create array of object type and cast it to your type 创建对象类型数组并将其强制转换为您的类型
Object[] arr=new Object[]{this.new MyClass(), this.new MyClass()};
MyClass[] myArray = Arrays.copyOf(arr,arr.length, Item.MyClass[].class);
If you make it static it will work because- A static nested class or nested interface (which is always static, by the way) has no relation to its outer class (or interface) apart from namespace nesting and access to private variables . 如果你将它设置为静态它将起作用,因为- 静态嵌套类或嵌套接口(顺便说一下,它始终是静态的)除了命名空间嵌套和访问私有变量之外与它的外部类(或接口)没有关系 。 As an example in the standard API, look for the interface Map.Entry, nested inside the interface Map, yet has no access to its type parameters and needs to declare them again.
作为标准API中的示例,查找嵌套在Map接口内的接口Map.Entry,但是无法访问其类型参数,需要再次声明它们。
The JLS section that covers this is 10.6 . 涵盖此内容的JLS部分是10.6 。 Specifically, it's because:
具体来说,这是因为:
It is a compile-time error if the ClassOrInterfaceType does not denote a reifiable type (§4.7).
如果ClassOrInterfaceType不表示可重新类型(第4.7节),则为编译时错误。 Otherwise, the ClassOrInterfaceType may name any named reference type, even an abstract class type (§8.1.1.1) or an interface type (§9).
否则,ClassOrInterfaceType可以命名任何命名引用类型,甚至是抽象类类型(第8.1.1.1节)或接口类型(第9节)。
The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard.
上述规则意味着数组创建表达式中的元素类型不能是参数化类型,而不是无界通配符。
Because MyClass
is non-static it is dependent on the outer class; 因为
MyClass
是非静态的,所以它依赖于外部类; it's actually GenericClass<T>.MyClass
and therefore a parameterized type. 它实际上是
GenericClass<T>.MyClass
,因此是参数化类型。 Declaring it static
removes that dependency and solves the problem. 将其声明为
static
会删除该依赖项并解决问题。
Where it gets weird is if you do this; 如果你这样做,那会很奇怪;
class MyClass<T> {
}
public GenericClass(final T[] param) {
MyClass[] myArray = { new MyClass(), new MyClass() };
}
It's legal. 这是合法的。 Screwy, kind of clumsy, but legal.
笨拙,有点笨拙但合法。 Because you redeclare the type, it hides the outer one.
因为你重新声明了类型,它隐藏了外部类型。 Then ... arrays and generics don't mix ... unless you use raw types.
然后...数组和泛型不混合......除非你使用原始类型。 For backward compatibility you can have a rawtype array which ends up holding
MyClass<Object>
. 为了向后兼容,您可以拥有一个rawtype数组,最终持有
MyClass<Object>
。 It's a really awful thing, but it does compile. 这是一个非常糟糕的事情,但它确实编译。 You can get away with creative casting here but in the end ... just ... don't.
你可以在这里摆脱创意铸造,但最终......只是......不要。
The problem here is that the compiler cannot determine at compile time the information of the array myArray. 这里的问题是编译器无法在编译时确定数组myArray的信息。 It is considered generic because (as eclipse shows you) it is converted in {new GenericClass<T>.MyClass(), ...}.
它被认为是通用的,因为(正如eclipse所示)它在{new GenericClass <T> .MyClass(),...}中转换。 This is because you're putting the class MyClass inside a generic class.
这是因为您将类MyClass放在泛型类中。
This code doesn't work either: 此代码也不起作用:
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();
}
}
but this code works: 但是这段代码有效:
package my.stuff;
public class GenericClass<T> {
public GenericClass(final T[] param) {
MyClass myObject = new MyClass();
MyClass[] myArray = { new MyClass(), new MyClass() };
}
}
class MyClass {
}
Because you're not using generics in your MyClass, the best thing to do is probably the second one. 因为你没有在你的MyClass中使用泛型,所以最好的办法可能是第二个。
If you declare it static, the compiler knows that MyClass is not generic and it knows what to do. 如果将其声明为静态,则编译器知道MyClass不是通用的,并且它知道该怎么做。
Besides, the only way to create a generic array in java is create a raw type and then cast it to generics (see here: "Cannot create generic array of .." - how to create an Array of Map<String, Object>? ). 此外,在java中创建泛型数组的唯一方法是创建一个原始类型,然后将其强制转换为泛型(参见此处: “无法创建...的通用数组” - 如何创建Map <String,Object>的数组? )。 So, if you absolutely need myClass inside the generic one, you should turn it in MyClass<T>, and then you use the trick: create a raw type and cast it to MyClass<T>:
所以,如果你绝对需要myClass里面的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>() };
}
}
even it you don't use T inside the class MyClass. 即使你不在MyClass类中使用T.
@ItayMaman has the right reason. @ItayMaman有正确的理由。 Basically,
MyClass
is not a reifiable type. 基本上,
MyClass
不是可再生的类型。
MyClass
is a non-static inner class. MyClass
是一个非静态的内部类。 Since it is non-static, it is within the scope of the type parameters of its enclosing class. 由于它是非静态的,因此它在其封闭类的类型参数范围内。 And every time you write
MyClass
by itself in an instance method of GenericClass
, it is actually short for GenericClass<T>.MyClass
. 每次在
GenericClass
的实例方法中自己编写MyClass
,它实际上都是GenericClass<T>.MyClass
缩写。 So even though it may not look it, MyClass
(by itself) is actually a parameterized type (parameterized by T
), similar to List<String>
. 因此,即使它看起来不像,
MyClass
(本身)实际上是一个参数化类型(由T
参数化),类似于List<String>
。 And so when you do new MyClass[2]
, you are trying to create an array of a parameterized type, just like new List<String>[2]
. 所以当你做
new MyClass[2]
,你试图创建一个参数化类型的数组,就像new List<String>[2]
。 And I think you already know that this is not allowed. 我想你已经知道这是不允许的。
What should you do? 你该怎么办? It all depends on what your intention is.
这一切都取决于你的意图。 One thing that people suggest is to make
MyClass
static. 人们建议的一件事是让
MyClass
静态。 Of course, that will take it out of the scope of T
. 当然,这将超出
T
的范围。 But that may or may not be what you want, because it completely changes its relation to GenericClass
. 但这可能是也可能不是你想要的,因为它完全改变了它与
GenericClass
关系。 A non-static inner class has access to an instance of the enclosing class, which is perhaps why you made it that way in the first place. 非静态内部类可以访问封闭类的实例,这也许就是为什么你首先这样做的原因。 If you never intended for it to be non-static (and did it by mistake), then this is obviously the way to go.
如果你从来没有打算让它成为非静态的(并且错误地做了),那么这显然是要走的路。
If a non-static inner class is what you want, and you simply wants to create an array of this type, let's consider how you would usually deal with arrays of parameterized types, eg List<String>[]
. 如果你想要一个非静态内部类,并且你只想创建一个这种类型的数组,那么让我们考虑你通常如何处理参数化类型的数组,例如
List<String>[]
。
One solution is to instead create an array of the raw type, eg List[] foo = new List[2];
一种解决方案是改为创建原始类型的数组,例如
List[] foo = new List[2];
. 。 The equivalent way to do this for our case would be
GenericClass.MyClass[] foo = new GenericClass.MyClass[2];
对我们的案例执行此操作的等效方法是
GenericClass.MyClass[] foo = new GenericClass.MyClass[2];
. 。 Notice what we did here.
注意我们在这里做了什么。 In order to write the raw type, we had to explicitly qualify
MyClass
with the unparameterized outer class name. 为了编写原始类型,我们必须使用未参数化的外部类名明确限定
MyClass
。 If we didn't explicitly qualify it, then it would be implicitly qualified with GenericClass<T>
, as explained above, which is not what we want. 如果我们没有明确限定它,那么它将被
GenericClass<T>
隐式限定,如上所述,这不是我们想要的。 Translating this to the code in your example, you would write GenericClass.MyClass[] myArray = { new MyClass(), new MyClass() };
将此转换为示例中的代码,您将编写
GenericClass.MyClass[] myArray = { new MyClass(), new MyClass() };
Similarly, if we want to avoid raw types, we could create an array of the wildcarded type, eg List<?>[] foo = new List<?>[2];
类似地,如果我们想要避免原始类型,我们可以创建一个通配类型的数组,例如
List<?>[] foo = new List<?>[2];
. 。 The equivalent way to do this for our case would be
GenericClass<?>.MyClass[] foo = new GenericClass<?>.MyClass[2];
对我们的案例执行此操作的等效方法是
GenericClass<?>.MyClass[] foo = new GenericClass<?>.MyClass[2];
. 。 So translating this to the code in your example, you would write
GenericClass<?>.MyClass[] myArray = { new MyClass(), new MyClass() };
因此,将其转换为示例中的代码,您将编写
GenericClass<?>.MyClass[] myArray = { new MyClass(), new MyClass() };
Finally, we might instead want to create an array of the wildcarded type, but then cast back into an array of the parameterized type, for convenience of use later on. 最后,我们可能想要创建一个通配类型的数组,但后来转换回参数化类型的数组,以方便以后使用。 eg
List<String>[] foo = (List<String>[])new List<?>[2];
例如
List<String>[] foo = (List<String>[])new List<?>[2];
. 。 The equivalent way to do this for our case would be
MyClass[] myArray = (MyClass[])new GenericClass<?>.MyClass[] { new MyClass(), new MyClass() };
对我们的案例执行此操作的等效方法是
MyClass[] myArray = (MyClass[])new GenericClass<?>.MyClass[] { new MyClass(), new MyClass() };
. 。 Note the the cast is an unchecked cast.
请注意,演员表是未选中的演员。 The advantage of this is now when you get things out of
myArray
, it will be type MyClass
, instead of raw type GenericClass.MyClass
or wildcarded type GenericClass<?>.MyClass
from the methods above. 这样做的好处是,当你从
myArray
获取东西时,它将是MyClass
类型,而不是原始类型GenericClass.MyClass
或通配类型GenericClass<?>.MyClass
来自上述方法的GenericClass<?>.MyClass
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.