内部类的通用数组创建

时间:2015-01-15 17:20:16

标签: java generics inner-classes

我创建数组的行给了Generic array creation警告。 处理这个问题的好方法是什么?

public class Foo<T> {

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

    class Point {
        float x, y;
    }
}

4 个答案:

答案 0 :(得分:8)

首先,让我们弄清楚Java认为new Point[3]创建通用数组的原因,而Point似乎是非泛型类。发生这种情况是因为Point是非静态类,这意味着它具有对编译器嵌入的Foo<T>的隐藏引用。该类看起来像Java:

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

程序文本中没有Foo$<T>_hidden_Foo,但编译器认为它们是,因为Point是一个内部类通用类Foo<T>

有两种方法可以解决这个问题:

  • 您可以static为您的班级Point,假设这是您打算做的。见ajb的答案。但是,Point的任何实例方法都无法再访问Foo<T>成员
  • 如果static不是选项,请将数组替换为List<Point>或其他适合您需要的集合。该限制仅适用于通用数组,但通用集合很好。

以下是如何使用集合:

public class Foo<T> {
    void someMethod() {
        List<Point> points = new ArrayList<Point>();
        ... // add three points to the list
    }
    class Point {
        float x, y;
    }
}

答案 1 :(得分:3)

在我看来,您的Point课程就在那里举行xy,并且没有理由对其实例进行隐藏引用Foo<T>。如果这是正确的,那么Point应该是嵌套类,而不是内部类。添加static关键字:

public class Foo<T> {

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

    static class Point {
        float x, y;
    }
}

答案 2 :(得分:2)

内部类也可以访问其外部类的泛型类型。让我们说我们有

class Foo<T> {

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

创建Foo的实例时

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

我们可以基于其外部实例(如

)创建其内部类的实例
Point p = f.new Point();
// or 
//Foo<String>.Point p = f.new Point 
// if we are creating it for instance outside of Foo class

并且编译器会知道p.getValue()返回String,因此它可以让我们使用p.getValue().charAt(0)

现在问题是generic type can't be used in any part of array type,这意味着我们不能使用:

  • T[size]
  • Foo<T>[size]
  • 甚至不是 Foo<T>.Point[size]

最后一个例子似乎是你的情况,因为

Point[] points = new Point[3];

相当于

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

您没有多少选择来解决此问题。

  1. 您可以明确表示您不希望通过编写

    来使用泛型类型
    Point[] points = new Foo.Point[3];// we got rid of <T>
    

    但请勿这样做,因为raw types are evil

  2. 更好的解决方案是避免数组并使用支持List<Point>等泛型的Collection。

    List<Point> points = new ArrayList<>();
    
  3. 但最好的解决办法就是简单地摆脱T对外层Foo的依赖。这可以通过使您的内部类静态来实现,这意味着它不需要其外部类的实例,因此它不需要知道它使用哪种泛型类型。
    所以你可以简单地使用

    static class Point {
        float x, y;
    }
    

    现在

    Point[] points = new Point[3]; 
    

    编译正常。

答案 3 :(得分:0)

Point是一个非静态的内部类。所以Point本身就是Foo<T>.Point,这是一种参数化类型。您不能new Point[3](与new Foo<T>.Point[3]相同),原因与您new ArrayList<T>[3]相同。

所以,让我们进行类比并询问,当你想做什么时你会做什么

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

有两种方法:

  1. 创建原始类型的数组:

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

  2. 或者,如果您不喜欢原始类型,请创建一个通配符参数化类型的数组:

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

  3. 所以在我们的案例中,我们有两个相同的解决方案:

    1. 创建原始类型的数组。但是,原始类型是什么?它不是Point,正如我们所发现的那样;因为那是隐式参数化的。相反,我们需要使用外部类名明确限定名称:Foo.Point

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

    2. 或者,如果您不喜欢原始类型,请创建一个通配符参数化类型的数组:

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