内部 class 的通用数组创建

Generic array creation of inner class

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

public class Foo<T> {

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

    class Point {
        float x, y;
    }
}

在我看来,您的 Point class 只是用来存放一个 x 和一个 y,没有理由隐藏它引用 Foo<T> 的实例。如果这是正确的,那么 Point 应该是嵌套的 class,而不是内部的 class。添加 static 关键字:

public class Foo<T> {

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

    static class Point {
        float x, y;
    }
}

首先,让我们弄清楚为什么Java认为new Point[3]创建了一个通用数组,而Point似乎是一个非通用数组class。发生这种情况是因为 Point 是一个非静态 class,这意味着它有一个由编译器嵌入的对 Foo<T> 的隐藏引用。 class 对 Java 来说是这样的:

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

Foo$<T>_hidden_Foo 不在程序的文本中,但编译器认为它们在,因为 Point 是一个内部class 的通用 class Foo<T>.

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

  • 您可以将 static 设为您的 class 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;
    }
}

内部 classes 也可以访问其外部 class 的通用类型。假设我们有

class Foo<T> {

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

当您创建 Foo 的实例时,例如

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

我们可以根据其外部实例创建其内部 class 的实例,例如

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() returns 字符串,所以它可以让我们使用 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. 更好的解决方案是避免使用数组并使用支持泛型的 Collection,例如 List<Point>.

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

    static class Point {
        float x, y;
    }
    

    现在

    Point[] points = new Point[3]; 
    

    编译正常。

Point 是一个非静态内部 class。所以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];

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

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

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

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

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