假设我有以下代码:
class A<THIS extends A> {
public THIS self() {
return (THIS) this;
}
}
class B extends A<B> { }
A a = new A<>().self().self().self(); // OK
B b = new B().self().self().self().self().self().self(); // OK
它编译良好。 但是,当我再添加一个继承级别时,它将不起作用。
class A<THIS extends A> {
public THIS self() {
return (THIS) this;
}
}
class B<THIS extends B> extends A<B> { }
class C extends B<C> { }
A<A> self2 = new A<A>().self().self().self(); // OK
B<B> self = new B<B>().self().self().self().self(); // error -> A
C self1 = new C().self().self(); // error -> A
我尝试了其他通用类型,但没有帮助。
我该怎么做才能编译此代码?
答案 0 :(得分:2)
这种方法是有缺陷的。
它不会阻止诸如这样的声明
class D extends A<B> {
}
将进行编译,但会在运行时引发异常,更具体:
new D().self().getClass() // => ClassCastException
要让类提供超出其已知超类的功能,您可以尝试使用 adapter 模式。
它的基础通常是一个接口,例如
interface Adaptable {
T getAdapter(Class<? extends T> key);
// for those who don't like to type long method names
default T as(Class<? extends T> key) {
return getAdapter(key);
}
}
一个实现可能看起来像
class A implements Adaptable {
@Override
public T getAdapter(Class<? extends T> key) {
/*
* To be less strict, one might also check for 'key.isInstance(this)',
* but it's an implementation decision.
*/
if(getClass() == key) {
return key.cast(this);
}
return null;
}
}
但是, adapter 模式允许提供其他对象,通常是目标上的专用视图,请参见下面的FileSource
示例。
此方法的主要缺点是客户端将始终必须检查适配器是否可用。但是,如果客户知道对象 是它正在寻找的子类,则可以简单地将其强制转换,这样我们就不会损失任何东西。也可以使用java.util.Optional
扩展该接口,但是基本思想保持不变。
interface Adaptable {
Optional<T> getAdapter(Class<? extends T> key);
}
对于一个示例用例,假设有一个Source
类,它为任何过程建模可用的源。我们知道源代码处理通常很棘手,因此很难归一化为单个类或接口,因此我们让Source
类实现了Adaptable
。
class Source implements Adaptable {
@Override
public Optional<T> getAdapter(Class<? extends T> key) {
if(getClass() == key) {
return Optional.of(key.cast(this));
}
return Optional.empty();
}
}
现在有一个基本的实现FileSource
,通常以java.io.File
的形式提供。
class FileSource extends Source {
private File pointer;
public File asFile() {
return pointer;
}
}
客户端现在可以检查源文件是否可用,并使用基础的java.io.File
进行某些操作。
Source source;
...
source.getAdapter(FileSource.class).ifPresent(fileSource -> {
File file = fileSource.asFile();
// do your magic with 'file'
});
更好的是,FileSource
可以简单地为File
提供一个适配器。此时,客户甚至不需要关心实现子类,而只需关心他/她实际想要的是什么。
class FileSource extends Source {
private File pointer;
@Override
public Optional<T> getAdapter(Class<? extends T> key) {
if(File.class == key) {
return Optional.of(key.cast(asFile()));
}
return super.getAdapter(key);
}
public File asFile() {
return pointer;
}
}
和
Source source;
...
source.getAdapter(File.class).ifPresent(file -> {
// do your magic with file
});
答案 1 :(得分:-1)
几小时的苦难后,我找到了正确的路。
class A<THIS extends A<THIS>> {
public THIS self() {
return (THIS) this;
}
}
class B<T extends B<T>> extends A<T> { }
class C<T extends C> extends B<C<T>> { }
A a = new A<>().self().self().self().self().self().self(); // OK
B b = new B<>().self().self().self().self().self().self(); // OK
C c = new C<>().self().self().self().self().self().self(); // OK
是的,它像@Izruo点一样不安全。