Java(SE8)不允许使用的通用类型数组如何工作?

时间:2018-12-08 08:41:27

标签: java

我正在尝试实现R-way Trie符号表,但是与此同时,我遇到了一个不寻常的问题,或者无法解决这个问题。让我解释一下我面临的问题:

package edu.nraj.dsalgo.rwaytrie;

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();

    private class Node{
        private Object value;
        private Node[] next = new Node[R];
    }

    public static void main(String[] args) {

    }

}

在此代码块中,您可以清楚地看到我正在创建一个内部私有类

private class Node{ ... }

但是现在Java se8编译器显示了

的问题
private Node[] next = new Node[R];

arrays of generic types not allowed. Node,我已经碰巧知道Java不允许通用类型数组。

The generic type array issue hint screenshot

但是,如果我将此Private Node class设为静态类,则编译器将停止抛出错误。

package edu.nraj.dsalgo.rwaytrie;

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();

    private static class Node{
        private Object value;
        private Node[] next = new Node[R];
    }

    public static void main(String[] args) {

    }

}

No issue after adding static to Node class screenshot

有人可以从根本上解释这种行为,以便我能解决这个问题。

3 个答案:

答案 0 :(得分:1)

以最简单的方式,当您使一个类通用时,它的所有内部类也是通用的,因为它们共享外部类信息。您可能已经知道,

public class RWayTrieST<Value> {
    private class Node {}
}

public class RWayTrieST<Value> {
    private static class Node {}
}

以前是Node,它是一个内部类,必须将Node的实例绑定到RWayTrieST的实例。因此,如果您有foo = New RWayTrieST<String>()并且有bar = foo.new Node(),那么对于编译器来说,bar的类型实际上是RWayTrieST<String>.Node,因为外部类的通用信息是必需的。 / p>

但是,在后者中,Node的实例不与RWayTrieST的实例绑定,因为它是静态内部类。因此Node不共享外部类的通用信息,这意味着它不是通用的。

答案 1 :(得分:0)

如果认为原因在于隐藏的细节:

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();    
    private class Node{
        private Object value;
        private Node[] next = new Node[R];
    }   
}

实际上是:

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();    
    private class Node<Value>{
        private final RWayTrieST<Value> $PARENT;
        private Object value;
        private Node[] next = new Node[R]; // and so Node is Node<Value> by this.
    }   
}

$PARENT是一个合成字段(尽管不是确切名称),编译器添加了该字段以允许调用父级非静态方法。如果父类包含泛型,则$PARENT的类型为RWayTrieST<Value>

由于Value是来自类声明的,因此还需要将其传递给实例:

private static class Node<Value>

在声明中依此类推,因此为new Node<Value>[R]

如果您要解决此问题,

  • 如果不需要调用父级的非静态方法,请照常使用static class
  • 使用ArrayList
  • 如果需要调用父静态方法,则使Nodestatic并添加一个私有字段RWayTrieST<?>。您可能还会遇到其他问题。

答案 2 :(得分:0)

如果将内部类设为静态,则基本上会使Node不在乎泛型参数Value。如果Node不是静态的,则Node则“属于RWayTrieST的实例”,就像其他任何非静态成员一样。这意味着Node实例的RWayTrieST<String>Node实例的RWayTrieST<Integer>的类型将不同。

这好像Node本身有一个通用参数,不是吗? RWayTrieST<String>.NodeRWayTrieST<Integer>.Node是完全不同的类型(至少在编译时),就像ArrayList<String>ArrayList<Integer>一样。

根据here,您无法使用通用参数创建类型的数组,因为如果允许,可能会发生以下情况:

RWayTrieST<String> foo = ...;
RWayTrieST<Integer> bar = ...;
Object[] stringArray = foo.new Node<String>[];  // compiler error, but pretend it's allowed
stringArray[0] = foo.new Node<String>();   // OK
stringArray[1] = bar.new Node<Integer>();  // An ArrayStoreException should be thrown,
                                            // but the runtime can't detect it, because generic types are erased.