简体   繁体   English

为什么我们不能在泛型类中创建一个“Concrete”类数组?

[英]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.

相关问题 为什么我们不能在Spring中将抽象类自动连接到具体类中? - Why can't we autowire an abstract class inside a concrete class in Spring? 为什么我不能创建泛型类型的内部类的数组? - Why can't I create an array of an inner class of a generic type? 为什么我们不能在类中覆盖 - why can't we override inside a class 我们可以从具体的 class 创建一个匿名内部 class 吗? - Can we create an anonymous inner class from a concrete class? 创建泛型类的具体实例的数组 - Creating an array of concrete instances of a generic class 具体类不使用通用标识符进行编译 - Concrete class doesn't compile with a generic identifier 无法在Java中创建泛型类的对象数组 - Can't create a Object Array of Generic Class in Java 为什么我们不能创建Collections类的实例(不是Collection Interface)? - Why can't we create instance of Collections class (not Collection Interface)? 为什么我们不能在 java 的子 class 的方法之外访问 class 内部的父实例变量 class? - why can't we access parent class instance variables inside the class outside the method in child class in java? 为什么我不能将对象数组转换为通用 class 的通用子类的数组? - Why can't I cast an array of Objects to an array of a generic subclass of a generic class?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM