为分层实体设计界面
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
包含到流中,这里出现了问题。
以下行不正确,因为 this
是 HierarchicalEntity
类型,而不是 T
:
parentsBuilder.add(this); // type mismatch!
如何重新设计界面才能使 getAncestors()
在结果中包含 this
?
添加 this
失败,因为 HierarchicalEntity<T>
不一定是 T
;它可能是未知的子类型。但是,T
始终是 HierarchicalEntity<T>
,因为您是这样声明的。
将 getAncestors
和 Stream.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 所说,没有办法完全执行此操作。但是,如果您愿意假设接口按设计使用,您可以假设 this
是 T
的一个实例并简单地转换它:
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
的任何实例。所以,我想选择你的毒药吧。
这是创建自引用类型时反复出现的问题。在基类型(或接口)中,您不能强制 this
与 T
.
赋值兼容
当然,如果您确信所有子类型都满足该约束,则可以执行 this
到 T
的未经检查的转换。但是只要您需要 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
...
类型
我必须为分层实体设计接口:
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
包含到流中,这里出现了问题。
以下行不正确,因为 this
是 HierarchicalEntity
类型,而不是 T
:
parentsBuilder.add(this); // type mismatch!
如何重新设计界面才能使 getAncestors()
在结果中包含 this
?
添加 this
失败,因为 HierarchicalEntity<T>
不一定是 T
;它可能是未知的子类型。但是,T
始终是 HierarchicalEntity<T>
,因为您是这样声明的。
将 getAncestors
和 Stream.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 所说,没有办法完全执行此操作。但是,如果您愿意假设接口按设计使用,您可以假设 this
是 T
的一个实例并简单地转换它:
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
的任何实例。所以,我想选择你的毒药吧。
这是创建自引用类型时反复出现的问题。在基类型(或接口)中,您不能强制 this
与 T
.
当然,如果您确信所有子类型都满足该约束,则可以执行 this
到 T
的未经检查的转换。但是只要您需要 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
...