Java中CRTP的替代品 [英] Alternatives to CRTP in Java

查看:83
本文介绍了Java中CRTP的替代品的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

CRTP 模式允许模仿所谓的 自身类型 ,例如g.:

The CRTP pattern allows to emulate the so called self types in Java, e. 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 接口;当然,还有其他用例),我可以轻松地比较 C1 的两个实例:

With the above code (the Comparable interface has been chosen just for clarity; of course there're other use cases), I can easily compare two instances of C1:

new C1().compareTo(new C1());

但不是具体类型不同的 AbstractFoo 后代:

but not AbstractFoo descendants whose concrete types are different:

new C1().compareTo(new C2()); // compilation error

但是很容易滥用模式:

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

// ...

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

此外,类型检查纯粹是编译时检查,即.e.可以轻松地将 C1 C2 实例混合在单个 TreeSet 中,并将它们进行比较.

Also, the type checks are purely compile-time ones, i. e. one can easily mix C1 and C2 instances in a single TreeSet and have them compared with each other.

Java 中的 CRTP 有什么替代方法可以模拟自身类型,而不会出现如上所述的滥用可能性?

What alternative to CRTP in Java emulates self types without the potential for abuse as shown above?

P.S..我观察到该模式在标准库中并未得到广泛使用,只有 EnumSet 及其后代实现了该模式.

P. S. I observe the pattern is not widely used in the standard library -- only EnumSet and its descendants implement it.

推荐答案

我不认为您显示的内容是滥用".只要使用 AbstractFoo C3 的所有代码,只要它们不执行任何不安全的强制转换,它们仍然是完全类型安全的. AbstractFoo SELF 的边界意味着该代码可以依赖于 SELF AbstractFoo< SELF> ,但是代码不能依赖 AbstractFoo< SELF> SELF 的子类型的事实.因此,例如,如果 AbstractFoo 具有返回 SELF 的方法,并且该方法是通过返回 this 来实现的(如果确实有可能一种自我类型"),则无法编译:

I don't think what you showed is "abuse". All the code that uses AbstractFoo and C3 are still perfectly type-safe, as long as they don't do any unsafe casts. The bounds of SELF in AbstractFoo means that the code can rely on the fact that SELF is a subtype of AbstractFoo<SELF>, but the code cannot rely on the fact that AbstractFoo<SELF> is a subtype of SELF. So for example, if AbstractFoo had a method that returned SELF, and it was implemented by returning this (as should be possible if it were really a "self-type"), it would not compile:

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

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

The compiler doesn't let you compile this because it is unsafe. For example, running this method on C3 would return this (which is really a C3 instance) as type C1, which would cause a class cast exception in the calling code. If you tried to sneak past the compiler by using a cast, like return (SELF)this;, then you get an unchecked cast warning which means you take responsibility for it being unsafe.

如果您的 AbstractFoo 的使用方式仅依赖于 SELF扩展AbstractFoo< SELF> 的事实(如界说),则不会依赖于 AbstractFoo< SELF>扩展SELF ,那么为什么还要关心 C3 的滥用"?您仍然可以编写类 C1扩展AbstractFoo< C1> C2扩展AbstractFoo< C2> 即可.并且,如果其他人决定编写一个类,则C3扩展了AbstractFoo< C1> ,那么只要他们以不使用不安全类型转换的方式编写它,编译器就会保证它仍然是类型安全的.也许这样的课程可能无法做任何有用的事情.我不知道.但是它仍然是安全的.那为什么会有问题呢?

And if your AbstractFoo is really used in a way that only relied on the fact that SELF extends AbstractFoo<SELF> (as the bound says), and does not rely on the fact that AbstractFoo<SELF> extends SELF, then why do you care about the "abuse" of C3? You can still write your classes C1 extends AbstractFoo<C1> and C2 extends AbstractFoo<C2> fine. And if someone else decides to write a class C3 extends AbstractFoo<C1>, then as long as they write it in a way without using unsafe casts, the compiler guarantees that it is still type-safe. Perhaps such a class might not be able to do anything useful; I don't know. But it is still safe; so why is that a problem?

之所以不使用诸如< SELF®扩展AbstractFoo< SELF>> 的递归绑定的原因是,在大多数情况下,它没有比<SELF> .例如, Comparable 接口的type参数没有限制.如果有人决定编写一个类,则扩展Comparable< Bar> ,他们就可以这样做,并且它是类型安全的,尽管不是很有用,因为在大多数使用 Comparable的类和方法中,,它们具有类型变量< T扩展Comparable< ;?超级T>> ,它要求 T 与自身具有可比性,因此 Foo 类在任何这些地方都不能用作类型参数.但是,如果有人愿意,可以写 Foo扩展Comparable< Bar> 还是可以的.

The reason why a recursive bound like <SELF extends AbstractFoo<SELF>> is not used much is that, in most cases, it is not any more useful than <SELF>. For example, the Comparable interface's type parameter doesn't have a bound. If someone decides to write a class Foo extends Comparable<Bar>, they can do so, and it is type-safe, though not very useful, because in most classes and methods that use Comparable, they have a type variable <T extends Comparable<? super T>>, which requires that T is comparable to itself, so the Foo class would not be usable as a type argument in any of those places. But it is still fine for someone to write Foo extends Comparable<Bar> if they want.

< SELF®这样的递归绑定扩展AbstractFoo< SELF>> 的唯一位置实际上是利用 SELF 扩展 AbstractFoo< SELF> ,这非常罕见.一个地方就是诸如构建器模式之类的东西,它具有返回对象本身的方法,这些方法可以被链接.因此,如果您有类似的方法

The only places where a recursive bound like <SELF extends AbstractFoo<SELF>> is in a place which actually makes use of the fact that SELF extends AbstractFoo<SELF>, which is quite rare. One place is in something like the builder pattern, which has methods that return the object itself, which can be chained. So if you have methods like

abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> {
    public SELF foo() { }
    public SELF bar() { }
    public SELF baz() { }
}

,您的一般值为 AbstractFoo<?>.x ,则可以执行 x.foo().bar().baz()之类的操作,如果将其声明为抽象类AbstractFoo< SELF> ;,则无法执行..

and you had a general value of AbstractFoo<?> x, you can do things like x.foo().bar().baz() which you could not do if it were declared as abstract class AbstractFoo<SELF>.

Java泛型中没有一种方法可以使类型参数必须与当前实现类的类型相同.如果假设有这样一种机制,那么可能会导致继承方面的棘手问题:

There isn't a way in Java Generics to make a type parameter that must be the same type as the current implementing class. If, hypothetically, there were such a mechanism, that could cause tricky problems with inheritance:

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 必须是实现类类型的约定.如果 SubC1.compareTo()必须接受 C1 自变量,那么接收到的事物的类型与当前对象本身的类型不再是正确的.如果 SubC1.compareTo()可以接受 SubC1 参数,则它不再覆盖 C1.compareTo(),因为它不再需要与超类中的方法所需要的参数一样宽.

Here, SubC1 implicitly inherits AbstractFoo<C1>, but that breaks the contract that SELF must be the type of the implementing class. If SubC1.compareTo() must take a C1 argument, then it is no longer true that the type of the thing received is the same type as the current object itself. If SubC1.compareTo() can take a SubC1 argument, then it is no longer overrides C1.compareTo(), as it no longer takes as wide a set of the arguments as the method in the superclass takes.

这篇关于Java中CRTP的替代品的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆