Java泛型自引用:它安全吗?

Mic*_*tti 16 java generics this self-reference

我有这个简单的界面:

public interface Node<E extends Node<E>>
{
    public E getParent();

    public List<E> getChildren();

    default List<E> listNodes()
    {
        List<E> result = new ArrayList<>();

        // ------> is this always safe? <-----
        @SuppressWarnings("unchecked")
        E root = (E) this;

        Queue<E> queue = new ArrayDeque<>();
        queue.add(root);

        while(!queue.isEmpty())
        {
            E node = queue.remove();

            result.add(node);

            queue.addAll(node.getChildren());
        }

        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

我看到它this总是Node<E>(根据定义)的一个实例.
但是我无法想象一个this不是实例的情况E......
因为E extends Node<E>,不应该也不Node<E>等同于E定义?

你能给出一个实例的对象的例子Node<E>,但它不是一个实例E吗?

与此同时,我的大脑正在融化......


上一课是一个简化的例子.
为了说明为什么我需要一个自我约束,我增加了一些复杂性:

public interface Node<E extends Node<E, R>, R extends NodeRelation<E>>
{
    public List<R> getParents();

    public List<R> getChildren();

    default List<E> listDescendants()
    {
        List<E> result = new ArrayList<>();

        @SuppressWarnings("unchecked")
        E root = (E) this;

        Queue<E> queue = new ArrayDeque<>();
        queue.add(root);

        while(!queue.isEmpty())
        {
            E node = queue.remove();

            result.add(node);

            node.getChildren()
                .stream()
                .map(NodeRelation::getChild)
                .forEach(queue::add);
        }

        return result;
    }
}

public interface NodeRelation<E>
{
    public E getParent();

    public E getChild();
}
Run Code Online (Sandbox Code Playgroud)

ern*_*t_k 12

一个简单的例子来说明问题:不同类型节点的节点:

class NodeA implements Node<NodeA> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

和:

class NodeB implements Node<NodeA> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,E root = (E) this将解决NodeA root = (NodeA) this,在哪里this是NodeB.这是不相容的.

  • 而已!谢谢! (2认同)
  • 我不确定这是对的.我打算发布类似的东西,但后来我测试了它,并且`E root =(E)this;`不会抛出异常.`E root =(E)this;由于类型擦除,``没有解析为`NodeA root =(NodeA)this`.如果我没弄错的话,它只能解析为`Node root =(Node)this;`@MicheleMariotti (2认同)
  • @ernest_k我刚看到一个字节码,你对类型擦除是正确的,'E root =(E)this;' - 实际上编译器将忽略该强制转换.它仅在通过特定参数解析泛型时才转换对象,就像我们迭代结果列表时一样. (2认同)