Why can't we create an array of the 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 does not create a shared array. The compiler should not have problems understanding / defining MyClass , right?

+4
source share
6 answers

Inner classes โ€œknowโ€ which instance of the surrounding class created them, and can refer to the fields / members of this instance. It is as if they had a second this variable, whose type is the specific type of the enclosing class (for example, GenericClass<String> ).

To overcome this predicament, you can make MyClass static . This will make it completely separate from any instance of the enclosing class (i.e.: it will not have a second this ) so that they can be created freely:

 public class GenericClass<T> { static class MyClass { } public GenericClass(final T[] param) { MyClass myObject = new MyClass(); // OK MyClass[] myArray = { new MyClass(), new MyClass() }; } } 
+2
source

Here is additional additional information . Link...

Java arrays contain runtime type information that identifies the type of elements contained

for the compiler, your code is as follows:

 MyClass[] myArray = {new GenericClass<T>.MyClass(), ..} //T is unknown 
+1
source
 { new MyClass(), new MyClass() }; //new MyClass() => new GenericClass<T>.MyClass() 

Above, the code will be considered as an array of the object, since T is unknown, due to the way the generic tools are implemented (by erasing), the type of the array is not defined correctly . On the one hand, it must be an array of MyClass, on the other hand, it must be an array of Object

Create an array of the type of the object and enter it into 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, a static nested class or nested interface (which is always static, by the way) has nothing to do with its outer class (or interface), except for naming of the namespace and access to private variables . As an example, in the standard API, find the Map.Entry interface, nested inside the interface map, but without access to its type parameters and must declare them again.

+1
source

The JLS section that covers this is 10.6 . In particular, this is because:

This is a compile-time error if the ClassOrInterfaceType class does not indicate a re-identifiable type (ยง4.7). Otherwise, ClassOrInterfaceType can call any named reference type, even an abstract class type (ยง8.1.1.1) or an interface type (ยง9).

It follows from the above rules that the type of an element in an array creation expression cannot be a parameterized type other than an unlimited wildcard.

Since MyClass non-static, it depends on the outer class; it is actually a GenericClass<T>.MyClass and therefore a parameterized type. A static declaration eliminates this dependency and solves the problem.

If this is strange, if you do,

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

It is legal. Modest, some clumsy, but legal. Since you are overriding a type, it hides the external. Then ... arrays and generics don't mix ... unless you use raw types. For backward compatibility, you can have a rawtype array that ends with holding MyClass<Object> . It is really terrible, but it compiles. You can get away from creative casting here, but in the end ... just ... don't do it.

+1
source

The problem is that the compiler cannot determine the information of the myArray array during compilation. It is considered common because (as the eclipse shows) it is converted to {new GenericClass <T> .MyClass (), ...}. This is because you put the MyClass class inside a common class.

This code does not work:

 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 { } 

Since you are not using generics in your MyClass, it is best to probably do the second.

If you declare it static, the compiler knows that MyClass is not shared, and it knows what to do.

In addition, the only way to create a shared array in java is to create a raw type and then pass it to generics (see here: "Cannot create a shared array .."; - how to create an array from Map <String, Object>? ). So, if you absolutely need myClass inside the generic, you have to rotate it to MyClass <T>, and then use the trick: create a raw type and pass it to 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 if you are not using T inside the MyClass class.

0
source

@ItayMaman has the right reason. In principle, MyClass not a reproducible type.

MyClass is a non-static inner class. Since it is non-static, it is included in the scope of parameters such as its spanning class. And each time you write MyClass yourself in the GenericClass instance GenericClass , this is actually short for GenericClass<T>.MyClass . Therefore, even if it does not look like this, MyClass (by itself) is actually a parameterized type (parameterized by T ), similar to List<String> . So, when you make new MyClass[2] , you are trying to create an array of parameterized type, like new List<String>[2] . And I think you already know that this is not allowed.

What should you do? It all depends on your intention. One thing that people offer is to make MyClass static. Of course, this will lead him out of the region T But this may or may not be what you want, because it completely changes its attitude to GenericClass . A non-static inner class has access to an instance of the enclosing class, which is probably why you did it this way. If you never planned for it to be unsteady (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 just want to create an array of this type, let's look at how you usually feel about arrays of parameterized types, for example. List<String>[] .

  • One solution is to create an array of the raw type, for example. List[] foo = new List[2]; . A similar way to do this for our case would be GenericClass.MyClass[] foo = new GenericClass.MyClass[2]; . Pay attention to what we have done here. To write a raw type, we had to explicitly qualify MyClass with the non-parameterized name of the outer class. If we had not explicitly qualified it, then it would have been implicitly qualified using GenericClass<T> , as explained above, which we do not want. Translating this into the code in your example, you should write GenericClass.MyClass[] myArray = { new MyClass(), new MyClass() };

  • Similarly, if we want to avoid raw types, we could create an array of a wildcard type, for example. List<?>[] foo = new List<?>[2]; . A similar way to do this for our case would be GenericClass<?>.MyClass[] foo = new GenericClass<?>.MyClass[2]; . So, translating this into the code in your example, you should write GenericClass<?>.MyClass[] myArray = { new MyClass(), new MyClass() };

  • Finally, we can instead create an array of the wildcard type, but then return to the array of the parameterized type for ease of use later. for example List<String>[] foo = (List<String>[])new List<?>[2]; . A similar way to do this for our case would be MyClass[] myArray = (MyClass[])new GenericClass<?>.MyClass[] { new MyClass(), new MyClass() }; . Please note that the throw is an unchecked throw. The advantage of this is that now when you retrieve things from myArray , it will be the MyClass type instead of the raw GenericClass.MyClass type or the GenericClass<?>.MyClass wildcard type from the above methods.

0
source

Source: https://habr.com/ru/post/1492429/


All Articles