为分层实体设计界面

Designing interface for hierarchical entity

我必须为分层实体设计接口:

interface HierarchicalEntity<T extends HierarchicalEntity<T>> {
    T getParent();
    Stream<T> getAncestors();
}

根据 getParent() 实现 default getAncestors() 方法非常容易,前者会 return Stream 所有祖先。

实现示例:

default Stream<T> getAncestors() {
    Stream.Builder<T> parentsBuilder = Stream.builder();
    T parent = getParent();
    while (parent != null) {
        parentsBuilder.add(parent);
        parent = parent.getParent();
    }
    return parentsBuilder.build();
}

但我还需要将 this 包含到流中,这里出现了问题。 以下行不正确,因为 thisHierarchicalEntity 类型,而不是 T:

parentsBuilder.add(this); // type mismatch!

如何重新设计界面才能使 getAncestors() 在结果中包含 this

添加 this 失败,因为 HierarchicalEntity<T> 不一定是 T;它可能是未知的子类型。但是,T 始终是 HierarchicalEntity<T>,因为您是这样声明的。

getAncestorsStream.Builder 的 return 类型从 T 更改为 HierarchicalEntity<T>,这将允许您添加 this.

default Stream<HierarchicalEntity<T>> getAncestors() {
    Stream.Builder<HierarchicalEntity<T>> parentsBuilder = Stream.builder();
    T parent = getParent();
    while (parent != null) {
        parentsBuilder.add(parent);
        parent = parent.getParent();
    }
    parentsBuilder.add(this);
    return parentsBuilder.build();
}

为了保持一致性,您可能希望将 getParent 声明为 return 和 HierarchicalEntity<T>

正如@SotiriosDelimanolis 所说,没有办法完全执行此操作。但是,如果您愿意假设接口按设计使用,您可以假设 thisT 的一个实例并简单地转换它:

parentsBuilder.add((T)this);

如果想避免强制转换,可以在子类中添加一个要重写的方法:

interface HierarchicalEntity<T extends HierarchicalEntity<T>> {
    T getParent();
    T getThis();
    default Stream<T> getAncestors() {
        // ...
        parentsBuilder.add(getThis());
        // ...
    }
}

class Foo extends HierarchicalEntity<Foo> {
    // ...
    @Override
    public Foo getThis() {
        return this;
    }
}

现在我们可以以类型安全的方式获取 this,但不能保证 getThis() 已正确实现。它可以 return Foo 的任何实例。所以,我想选择你的毒药吧。

这是创建自引用类型时反复出现的问题。在基类型(或接口)中,您不能强制 thisT.

赋值兼容

当然,如果您确信所有子类型都满足该约束,则可以执行 thisT 的未经检查的转换。但是只要您需要 this 引用作为 T.

,就必须执行此未经检查的转换

更好的解决方案是添加一个抽象方法,如

/**
    All subtypes should implement this as:

    public T myself() {
        return this;
    }
 */
public abstract T myself();

然后,只要您需要自引用 T.

,就可以使用 myself() 而不是 this
default Stream<T> getAncestors() {
    Stream.Builder<T> parentsBuilder = Stream.builder();
    for(T node = myself(); node != null; node = node.getParent()) {
        parentsBuilder.add(parent);
    }
    return parentsBuilder.build();
}

当然,您不能强制子 class 将 myself() 正确地实现为 return this;,但至少,您可以轻松验证它们是否在运行时执行:

assert this == myself();

这个引用比较是一个非常便宜的操作,如果 myself() 被正确地实现为总是返回 this,HotSpot 可以提前证明这个比较总是 true 并且完全取消检查。

缺点是每个专业化都必须有 myself() { return this; } 的冗余实现,但另一方面,它完全没有未经检查的类型转换。另一种方法是在基础 class 中将 myself() 的非 abstract 声明为 @SuppressWarnings("unchecked") T myself() { return (T)this; },以将未经检查的操作限制在类型层次结构的单个位置。但是,你无法验证 this 是否真的是 T...

类型