5

The line where I create the array gives me a Generic array creation warning. What is a good way to deal with this?

public class Foo<T> {

    void someMethod() {
        Point[] points = new Point[3];
    }

    class Point {
        float x, y;
    }
}
Software Engineer
  • 15,457
  • 7
  • 74
  • 102
clankill3r
  • 9,146
  • 20
  • 70
  • 126
  • 7
    `static class Point {...`. It looks like your inner class is just there to hold an `x` and `y`, and doesn't really need a hidden pointer to an instance of a `Foo`. If I'm right, then it should be a nested class and not an inner class. – ajb Jan 15 '15 at 17:22
  • 1
    @ajb That should be an answer (because it's the answer). – Elliott Frisch Jan 15 '15 at 17:28
  • 1
    What is interesting is why `Point p = new Point()` compiles fine, while `Point[] points = new Points[3]` doesn't. – Pshemo Jan 15 '15 at 17:31
  • 1
    @Pshemo Even `List points = new ArrayList()` compiles fine, the restriction applies only to arrays. – Sergey Kalinichenko Jan 15 '15 at 17:43

4 Answers4

9

First, let's figure out the reason why Java thinks that new Point[3] creates a generic array, while Point appears to be a non-generic class. This happens because Point is a non-static class, meaning that it has a hidden reference to Foo<T> embedded by the compiler. The class looks like this to Java:

class Foo$Point<T> {
    Foo<T> _hidden_Foo;
    float x, y;
}

The Foo$, <T> and _hidden_Foo are not there in the text of your program, but the compiler thinks that they are, because Point is an inner class of a generic class Foo<T>.

There are two ways of fixing this problem:

  • You could make static your class Point, assuming that this is what you intended to do. See ajb's answer. However, any instance methods of Point would no longer be able to access Foo<T>'s members
  • If static is not an option, replace the array with a List<Point> or another collection suitable to your needs. The restriction applies only to generic arrays, but generic collections are fine.

Here is how you can use a collection:

public class Foo<T> {
    void someMethod() {
        List<Point> points = new ArrayList<Point>();
        ... // add three points to the list
    }
    class Point {
        float x, y;
    }
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I am not sure about `Foo$Point`. If I am not mistaken it would declare `T` in `Point` which would be different than one from `Foo`. Do you have any source which could confirm it? – Pshemo Jan 15 '15 at 18:15
  • @Pshemo I think the `T` in `Foo$Point` would be independent of `T` in `Foo`. I put `` for an illustration of why `Point` becomes generic, pretending that it's a top-level class. I did not intend to imply that Java compiler would generate code exactly like this, only that (1) there would be a reference to `Foo` of the outer context, and (2) that's why the `Point` class would be considered generic. – Sergey Kalinichenko Jan 15 '15 at 18:19
3

It appears to me that your Point class is just there to hold an x and a y, and there's no reason for it to have a hidden reference to an instance of a Foo<T>. If this is correct, then Point should be a nested class, instead of an inner class. Add the static keyword:

public class Foo<T> {

    void someMethod() {
        Point[] points = new Point[3];
    }

    static class Point {
        float x, y;
    }
}
ajb
  • 31,309
  • 3
  • 58
  • 84
3

Inner classes also have access to generic type of its outer class. Lets say that we have

class Foo<T> {

    class Point {
        float x, y;
        T value;
        T getValue(){
            return value;
        }
    }
}

When you create instance of Foo like

Foo<String> f = new Foo<>();

we can create instance of its inner class based on its outer instance like

Point p = f.new Point();
// or 
//Foo<String>.Point p = f.new Point 
// if we are creating it for instance outside of Foo class

and compiler will know that p.getValue() returns String, so it can lets us use p.getValue().charAt(0).

Now problem is that generic type can't be used in any part of array type, which means that we can't use:

  • T[size].
  • Foo<T>[size]
  • or not even Foo<T>.Point[size]

The last example seems to be your case because

Point[] points = new Point[3];

is equivalent of

Point[] points = new Foo<T>.Point[3];
//  Foo<T> is type of outer instance on which you are invoking new

You have few options to solve this problem.

  1. You can explicitly say that you don't want to use generic type by writing

    Point[] points = new Foo.Point[3];// we got rid of <T>
    

    but don't do that because raw types are evil.

  2. Better solution is to avoid arrays and use Collection which support generics like List<Point>.

    List<Point> points = new ArrayList<>();
    
  3. But probably best solution is to simply get rid of dependency of T from outer class Foo. This can be achieved by making your inner class static, which means it will not require instance of its outer class, so it will not need to know about which generic type is used by it.
    So you can simply use

    static class Point {
        float x, y;
    }
    

    and now

    Point[] points = new Point[3]; 
    

    will compile fine.

Community
  • 1
  • 1
Pshemo
  • 122,468
  • 25
  • 185
  • 269
1

Point is a non-static inner class. So Point written by itself means Foo<T>.Point, a parameterized type. You can't do new Point[3] (which is the same as new Foo<T>.Point[3]), for the same reason you can't do new ArrayList<T>[3].

So let's take the analogy and ask, what do you do when you want to do

ArrayList<T>[] lists = new ArrayList<T>[3];

There are two ways:

  1. Create an array of the raw type:

    ArrayList<T>[] lists = new ArrayList[3];

  2. Or, if you don't like raw types, create an array of the wildcard-parameterized type:

    ArrayList<T>[] lists = (ArrayList<T>[])new ArrayList<?>[3];

So in our case, we have the same two solutions:

  1. Create an array of the raw type. However, what's the raw type? It's not Point, as we've found; because that's implicitly parameterized. Instead, we need to explicitly qualify the name with the outer class names: Foo.Point:

    Point[] points = new Foo.Point[3];

  2. Or, if you don't like raw types, create an array of the wildcard-parameterized type:

    Point[] lists = (Point[])new Foo<?>.Point[3];

Community
  • 1
  • 1
newacct
  • 119,665
  • 29
  • 163
  • 224