为什么使用静态嵌套类?

5

我有一份trie的示例代码。代码似乎没有编译错误。为什么要使用静态嵌套类node?当我删除Node嵌套类中的static并编译时,会出现错误private Node[] next = new Node[R]; 中创建泛型数组。到底发生了什么?

public class TrieST<Value> {
    private static final int R = 256;        // extended ASCII


    private Node root;      // root of trie
    private int N;          // number of keys in trie

    // R-way trie node
    private static class Node {
        private Object val;
        private Node[] next = new Node[R];
    }


    public TrieST() {
    }
}

4
您知道为什么不允许使用通用数组吗?例如,使用new ArrayList<String>[5];会产生错误。 - user253751
可能是重复的问题:如何创建一个通用数组? - Nir Alfasi
添加 static 关键字后,为什么就不会出现这种错误了? - Peterxwl
因为使用静态方法,你可以创建一个 Node[] next = new Node[R] 数组。而使用非静态内部类,则会创建一个与外部类实例相关联的具有泛型类型的 Node。而创建泛型数组是被禁止的。 - Nir Alfasi
1
请注意,“静态内部类”是一个自相矛盾的说法。嵌套类要么是静态的,要么是内部的。 - user207421
显示剩余2条评论
2个回答

2
假设在您的代码片段中,您使用了非静态内部类而不是像这样的静态嵌套类:private class Node,在这种情况下,您将尝试实例化一个Array,这是不可能的,因为泛型类不具有关于其类型的任何信息,而数组创建表达式指定元素类型。因此,使用静态嵌套类编译的原因是,这些类被认为是“顶级”类(在行为方面):静态嵌套类与其外部类(和其他类)的实例成员交互,就像任何其他顶级类一样。实际上,静态嵌套类在行为上是一个被嵌套在另一个顶级类中以方便打包的顶级类。现在,让我们考虑所有这些,并回到编译器显示的确切错误:无法创建TrieST<Value>.Node的通用数组,这意味着您要创建的array的类型是TrieST<Value>.Node,其运行时类型未知,因此可能插入不同类型到next数组中。更清晰和详细的示例可以在无法创建参数化类型的数组中找到。而静态嵌套类不像TrieST<Value>的内部类那样行为,因此在Node内部实例化数组将不是非法的,因为它不是TrieST<Value>.Node类型,它是Node类型(就像顶级类一样)。

你能否更详细地解释一下为什么和如何嵌套的静态类“suppress”被视为顶层类来消除这个错误? - Peterxwl
一个顶层类是一个非内部类,这意味着静态嵌套类和外部类之间没有隐式关系,因此实例化数组不会影响外部类(即您的泛型类)。 - Tarik
@Peterxwl,我刚刚添加了一个更新,请现在看一下。 - Tarik
  1. "一个静态嵌套类被认为是内部类" - 错误。它是一个内部静态类。
  2. "...它是类型为Node的(你的顶层类)对象" - 再次错误,静态类表现得像顶层类,但它不是一个顶层类。
- Nir Alfasi
1
在Java文档中,@alfas提到“静态嵌套类在行为上是一个顶级类”,这就是我说“被认为是”的原因。很明显它并不是一个顶级类!所以我引用了Java文档!但是我使用了“被认为”或“所谓的”这个术语来澄清这个问题。 - Tarik
1
“这是一个内部静态类。” 静态嵌套类不是内部类。 “内部类是未声明为静态的嵌套类。”。嵌套类可以是内部或静态的。 - Radiodef

1
因为使用静态方法时,您会创建一个包含 Node[] next = new Node[R] 的数组,而使用非静态内部类时,则会创建一个与外部类实例相关联的具有泛型类型的节点。创建泛型数组是被禁止的。
但是让我们退回几步:实例化一个非静态内部类的方法如下(示例):
class TrieST<V> {
    private static final int R = 256;        

    private Node root;      // root of trie
    private int N;          // number of keys in trie
    private TrieST<String> inst = new TrieST<String>(); // must create an instance of the outer class first

    // R-way trie node
    private class Node {
        private Object val;
        private TrieST<String>.Node next =  inst.new Node(); //must use an instance of the outer class to instantiate an object of the inner class
    }

    public TrieST() {
    }
}

现在,如果我们尝试将上面的实现从内部类的实例更改为数组,我们将得到通用数组创建,因为由于数组的协变性质(Shape []Triangle []super),禁止使用泛型类型创建数组,这与泛型的不变性质不兼容(List<Object>不是List<String>super)。在《Effective Java》中,Bloch提供了更详细的解释,如果您想深入了解可以参考该书。
如果您坚持使用内部类,可以通过使用Array.newInstance()来解决此限制,它可以创建只在运行时已知类型的数组,如下所示:
private Node[] next = (Node[]) Array.newInstance(Node.class, R);

如果我仍希望在非静态内部类中有一个'Node [] next',该怎么办? - Peterxwl
@Peterxwl 你可以通过以下方式欺骗编译器:private Node[] next = (Node[]) Array.newInstance(Node.class, R); 我会将其添加到答案中。 - Nir Alfasi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接