返回

在Java泛型中实现自引用:解决循环引用问题

java

Java泛型中的自引用(递归):解析问题

自引用泛型简介

泛型是一种强大的编程技术,它允许我们创建类型安全的代码,而无需明确指定类型参数。在 Java 中,泛型可以通过<> 符号来表示,其中泛型类型变量位于符号之间。

在某些情况下,我们可能需要使用自引用泛型,也就是泛型类型变量引用它自己。这被称为泛型的递归,并且允许我们创建能够自我参数化的类型。

问题

在 Java 中,实现泛型递归可能会遇到一些挑战。让我们考虑以下示例:

public interface Node<E extends Edge> {
    List<E> getOutgoingEdges();
    List<E> getIncomingEdges();
}

public interface Edge<N extends Node> {
    N getSource();
    N getTarget();
}

编译器将拒绝编译此代码,因为它导致了循环引用。为了解决这个问题,我们可以尝试以下方法:

public interface Node<E extends Edge<?>> {
    List<E> getOutgoingEdges();
    List<E> getIncomingEdges();
}

public interface Edge<N extends Node<?>> {
    N getSource();
    N getTarget();
}

然而,这种方法的问题是,它允许使用两个类来实现这些接口:

public class NodeImpl<E extends Edge<NodeImpl<E>>> implements Node<E> {
    // ...
}

public class EdgeImpl<N extends Node<EdgeImpl<N>>> implements Edge<N> {
    // ...
}

这并不是我们想要的,因为我们希望强迫 NodeImpl 和 EdgeImpl 仅能够实现特定的 Node 和 Edge 类型。

解决方案

正确的解决办法是:

public interface Node<E extends Edge<? super Node<E>>> {
    List<E> getOutgoingEdges();
    List<E> getIncomingEdges();
}

public interface Edge<N extends Node<? super N>> {
    N getSource();
    N getTarget();
}

这些接口使用? super 通配符,它允许类型参数引用其自身或其任何超类。这确保了 NodeImpl 和 EdgeImpl 只能使用兼容的 Node 和 Edge 类型进行实例化。

实例化

我们可以按如下方式实例化 NodeImpl 和 EdgeImpl:

NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<EdgeImpl<NodeImpl<Edge