gpt4 book ai didi

java - Java 中 CRTP 的替代方案

转载 作者:行者123 更新时间:2023-12-01 10:32:27 30 4
gpt4 key购买 nike

关闭。这个问题是opinion-based .它目前不接受答案。












想改进这个问题?更新问题,以便 editing this post 可以用事实和引用来回答它.

2年前关闭。




Improve this question




CRTP模式允许模拟所谓的 self types在 Java 中,例如。 G。:

abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> implements Comparable<SELF> {
@Override
public final int compareTo(final SELF o) {
// ...
}
}

final class C1 extends AbstractFoo<C1> {
// ...
}

final class C2 extends AbstractFoo<C2> {
// ...
}

使用上面的代码( Comparable 接口(interface)只是为了清楚起见;当然还有其他用例),我可以轻松比较 C1 的两个实例:
new C1().compareTo(new C1());

但不是 AbstractFoo具体类型不同的后代:
new C1().compareTo(new C2()); // compilation error

但是,很容易滥用该模式:
final class C3 extends AbstractFoo<C1> {
// ...
}

// ...

new C3().compareTo(new C1()); // compiles cleanly

此外,类型检查纯粹是编译时检查,即。 e.一个人可以轻松混合 C1C2单个 TreeSet 中的实例并让它们相互比较。

Java 中有什么替代 CRTP 的方法可以模拟 self 类型而没有滥用的可能性,如上所示?

附:我观察到该模式在标准库中没有被广泛使用——只有 EnumSet及其后代实现它。

最佳答案

我不认为你展示的是“滥用”。所有使用 AbstractFoo 的代码和 C3只要它们不进行任何不安全的强制转换,它们仍然是完全类型安全的。 SELF 的范围在 AbstractFoo意味着代码可以依赖 SELFAbstractFoo<SELF> 的子类型,但代码不能依赖 AbstractFoo<SELF>SELF 的子类型.例如,如果 AbstractFoo有一个方法返回 SELF ,并通过返回 this 来实现(如果它真的是一个“自我类型”应该是可能的),它不会编译:

abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> {
public SELF someMethod() {
return this; // would not compile
}
}

编译器不允许您编译它,因为它不安全。例如,在 C3 上运行此方法将返回 this (实际上是 C3 实例)作为类型 C1 ,这将导致调用代码中的类转换异常。如果您试图通过使用强制转换来绕过编译器,例如 return (SELF)this; ,然后你会收到一个未经检查的强制转换警告,这意味着你要为它不安全负责。

如果您的 AbstractFoo真正的使用方式仅依赖于 SELF extends AbstractFoo<SELF> (正如界所说),并且不依赖于 AbstractFoo<SELF> extends SELF 的事实。 ,那你为什么在乎 C3的“滥用”? ?你仍然可以写你的类(class) C1 extends AbstractFoo<C1>C2 extends AbstractFoo<C2>美好的。如果其他人决定写一门课 C3 extends AbstractFoo<C1> ,那么只要他们以不使用不安全强制转换的方式编写它,编译器就保证它仍然是类型安全的。也许这样的类可能无法做任何有用的事情;我不知道。但它仍然是安全的;那为什么会有问题呢?

<SELF extends AbstractFoo<SELF>> 这样的递归边界的原因用得不多的是,在大多数情况下,它并不比 <SELF> 有用。 .例如, Comparable接口(interface)的类型参数没有界限。如果有人决定写一门课 Foo extends Comparable<Bar> , 他们可以这样做,而且它是类型安全的,虽然不是很有用,因为在大多数使用 Comparable 的类和方法中,它们有一个类型变量 <T extends Comparable<? super T>> ,这要求 T与自身相当,所以 Foo class 在任何这些地方都不能用作类型参数。但是有人写 Foo extends Comparable<Bar>还是可以的如果他们愿意。

<SELF extends AbstractFoo<SELF>> 这样递归绑定(bind)的唯一地方是在一个真正利用 SELF 的地方。扩展 AbstractFoo<SELF> , 这是相当罕见的。一个地方是类似于构建器模式的东西,它具有返回对象本身的方法,可以链接。所以如果你有类似的方法
abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> {
public SELF foo() { }
public SELF bar() { }
public SELF baz() { }
}

你的一般值为 AbstractFoo<?> x ,您可以执行 x.foo().bar().baz() 之类的操作如果它被声明为 abstract class AbstractFoo<SELF>,你将无法做到这一点.

Java 泛型中没有办法创建必须与当前实现类具有相同类型的类型参数。如果假设有这样的机制,那么可能会导致继承的棘手问题:
abstract class AbstractFoo<SELF must be own type> {
public abstract int compareTo(SELF o);
}

class C1 extends AbstractFoo<C1> {
@Override
public int compareTo(C1 o) {
// ...
}
}

class SubC1 extends C1 {
@Override
public int compareTo(/* should it take C1 or SubC1? */) {
// ...
}
}

在这里, SubC1隐式继承 AbstractFoo<C1> , 但这违反了 SELF 的契约(Contract)必须是实现类的类型。如果 SubC1.compareTo()必须采取 C1论点,那么接收到的事物的类型与当前对象本身的类型不再相同。如果 SubC1.compareTo()可以拍 SubC1参数,则它不再覆盖 C1.compareTo() ,因为它不再需要像父类(super class)中的方法那样广泛的参数集。

关于java - Java 中 CRTP 的替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52611146/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com