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

This is not creating a generic array. The compiler should have no problems understanding/determining MyClass, isn't it?

midnite
  • 4,919
  • 7
  • 34
  • 51
  • 4
    Because `MyClass` isn't static. Your IDE should be telling you that. It has nothing to do with generics. – Brian Roach Jul 20 '13 at 05:19
  • @BrianRoach, Oh yeah! the problem is fixed! my lovely eclipse didnt tell me btw :-/ – midnite Jul 20 '13 at 05:21
  • @BrianRoach, But why? For non-generic outer class, the inner `MyClass` doesn't have to be `static`. Why it has to be `static` when the outer is generic? – midnite Jul 20 '13 at 05:23
  • Actually, I'll rescind the "it has nothing to do with generics" part. It does, sort of. If you define it `MyClass` you're fine. Trying to find the relevant spec. – Brian Roach Jul 20 '13 at 05:30
  • possible duplicate of http://stackoverflow.com/questions/2927391/whats-the-reason-i-cant-create-generic-array-types-in-java the exact answer is there. – Arif Samin Jul 20 '13 at 08:20

6 Answers6

2

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>).

To overcome this predicament you can make 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:

public class GenericClass<T> {

  static class MyClass {
  }

  public GenericClass(final T[] param) {
    MyClass myObject = new MyClass();                       // OK
    MyClass[] myArray = { new MyClass(), new MyClass() };   
  }
}
Paul Bellora
  • 51,514
  • 17
  • 127
  • 176
Itay Maman
  • 28,289
  • 9
  • 76
  • 114
1

Here's some additional information. From the link ...

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

to the compiler your code looks like this:

MyClass[] myArray = {new GenericClass<T>.MyClass(), ..} //T is unknown
ikumen
  • 9,715
  • 4
  • 37
  • 39
  • Hmm... but why the compiler says okay if `MyClass` is static? T is still unknown. – midnite Jul 20 '13 at 05:52
  • static fields/methods are known at compile time (static binding), where as instance fields/methods (which is how your MyClass was declared) of generic are dependent on type parameter which is known at run time (dynamic/late binding) – ikumen Jul 20 '13 at 06:11
1
{ 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. On one hand, it should be an array of MyClass , on the other hand, it should be an array of 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.

vikrant singh
  • 2,013
  • 1
  • 10
  • 15
1

The JLS section that covers this is 10.6. Specifically, it's because:

It is a compile-time error if the ClassOrInterfaceType does not denote a reifiable type (§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).

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; it's actually GenericClass<T>.MyClass and therefore a parameterized type. Declaring it static removes that dependency and solves the problem.

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>. 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.

Community
  • 1
  • 1
Brian Roach
  • 72,790
  • 10
  • 128
  • 154
  • I like the explanation and JLS citation, but the second part might confuse people - I'd just make it clear not to "fix" it that way. – Paul Bellora Jul 20 '13 at 14:32
0

The problem here is that the compiler cannot determine at compile time the information of the array myArray. It is considered generic because (as eclipse shows you) it is converted in {new GenericClass<T>.MyClass(), ...}. This is because you're putting the class MyClass inside a generic class.

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.

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

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>?). 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>:

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.

Community
  • 1
  • 1
Paolof76
  • 859
  • 1
  • 8
  • 23
0

@ItayMaman has the right reason. Basically, MyClass is not a reifiable type.

MyClass is a non-static inner class. 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. So even though it may not look it, MyClass (by itself) is actually a parameterized type (parameterized by T), similar to 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]. 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. Of course, that will take it out of the scope of T. But that may or may not be what you want, because it completely changes its relation to 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, e.g. List<String>[].

  • One solution is to instead create an array of the raw type, e.g. List[] foo = new List[2];. The equivalent way to do this for our case would be 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. 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. Translating this to the code in your example, you would write GenericClass.MyClass[] myArray = { new MyClass(), new MyClass() };

  • Similarly, if we want to avoid raw types, we could create an array of the wildcarded type, e.g. List<?>[] foo = new List<?>[2];. The equivalent way to do this for our case would be 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() };

  • 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. e.g. 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() };. 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.

newacct
  • 110,405
  • 27
  • 152
  • 217