[英]Avoiding generic types of form Foo<ActualType extends Foo<ActualType>>
我经常发现自己想要编写表单的通用类定义
public class Foo<ActualType extends Foo<ActualType>>
例如在这样的设置中:
public interface ChangeHandler<SourceType> {
public void onChange(SourceType source);
}
public class Foo<ActualType extends Foo<ActualType>> {
private final List<ChangeHandler<ActualType>> handlers = new ArrayList<>();
public void addChangeHandler(ChangeHandler<ActualType> handler) {
handlers.add(handler);
}
@SuppressWarnings("unchecked")
protected void reportChange() {
for (ChangeHandler<ActualType> handler: handlers)
handler.onChange((ActualType) this);
}
}
public class Bar extends Foo<Bar> {
// things happen in here that call super.reportChange();
}
public static void main(String[] args) throws IOException {
Bar bar = new Bar();
bar.addChangeHandler(new ChangeHandler<Bar>() {
@Override
public void onChange(Bar source) {
// Do something with the changed object
}
});
}
这里的 change-event 只是一个例子。 这更像是一个普遍的问题,每当我想允许超类为每个特定的子类提供“个性化”的功能时(不知道如何更好地表达这一点......在示例中)在“个性化”之上是这样一个事实,即ChangeHandler
是使用实际子类型 ( Bar
) 的对象而不是调用处理程序的超类 ( Foo
) 的类型调用的。
不知何故,这种方法对我来说似乎有点混乱。 它实际上允许潜在问题,因为没有什么可以阻止我定义:
public class Baz extends Foo<Bar> { /* ... */ }
有更清洁的替代品吗?
圣杯将是一些总是被定义为包含当前类的类型参数,比如this.getClass()
的静态版本,它允许我写这样的东西:
public class Foo {
private final List<ChangeHandler<this.Class>> handlers = new ArrayList<>();
public void addChangeHandler(ChangeHandler<this.Class> handler) {
handlers.add(handler);
}
protected void reportChange() {
for (ChangeHandler<this.Class> handler: handlers)
handler.onChange(this);
}
}
对于Bar
类型的类, this.Class
将等于Bar
。
这是一个非常抽象的问题。 在我看来,“如何使这个更清洁”的简短回答是:只在需要的地方使用泛型。
public class List<T extends List<T>>
这是什么意思表达(替代)? 一个只允许保持(T扩展)其他列表的列表,这些列表本身就是我们之前知道的Ts(List),它们只是允许持有的列表......等等。 有点循环,我不知道你最终会怎么样?
public interface ChangeHandler<SourceType> {
public void onChange(SourceType source);
}
你为什么要在这里使用泛型? 如果您想拥有一个可以处理多种资源类型的更改处理程序,那么您可以创建一个所有实际源继承的超类,或者创建一个由源实现的接口。 像这样,您可以准确指定源所公开的内容。 或者,源可以在通知时创建源对象,而不是传递“this”(然后它更像是一条消息)。 例如:
public interface ChangeHandler {
public void onChange(Source source);
}
public abstract class Source {
private List<ChangeHandler> handlers;
protected int nr;
public Source(int nr) {
this.nr = nr;
}
public abstract Something getSomething();
public String toString() {
return "SRC" + nr;
}
...
private notify(int index) {
handlers.get(i).onChange(this);
}
}
public class Foo extends Source {
public Foo(int nr) {
super(nr);
}
public String toString() {
return super.toString() + "Foo";
}
public Something getSomething() {
return new Something();
}
}
你永远不需要演员......或者你呢? 我不确定我是否理解这个问题。
我建议我们只使用<This>
来表示“自我类型”。 不需要绑定,因为它看起来很复杂,没有提供意图,并且无论如何都不能强制执行约束。
public class Foo<This> {
private final List<ChangeHandler<This>> handlers = new ArrayList<>();
public void addChangeHandler(ChangeHandler<This> handler) {
handlers.add(handler);
}
@SuppressWarnings("unchecked")
protected void reportChange() {
for (ChangeHandler<This> handler: handlers)
handler.onChange( (This)this );
}
}
注意演员(This)this
。
我从不使用类型参数来传递“ActualType”,因为这样就不可能扩展对象:
public class Bar extends Foo<Bar> {
// things happen in here that call super.reportChange();
}
public class Bar2 extends Bar{
// ...
}
Bar2 "ActualType" 仍然是 Bar,您无能为力:您将无法为 Bar2 使用 ChangeHandlers
为了避免这个问题,我看到的唯一可能的解决方法是将强制转换操作委托给另一个类(您也可以在 ChangeHandler 接口中使用默认方法)。 这是一种可能性:
public class Foo // no more type parameter
{
private final List<FooCaster<?>> casterHandlers = new ArrayList<>();
/**
* unsafe because you could register a ChangerHandler of any type.
* worst of all, everything is unchecked cast so the error could show up late.
*/
public <T> void addChangeHandler(ChangeHandler<T> handler) {
casterHandlers.add(new FooCaster<T>(handler));
}
protected void reportChange() {
for (FooCaster<?> caster: casterHandlers) {
caster.reportChange(this);
}
}
class FooCaster<T> {
protected ChangeHandler<T> ch;
protected FooCaster(ChangeHandler<T> ch) {
this.ch = ch;
}
@SuppressWarnings("unchecked")
public void reportChange(Foo f) {
ch.onChange((T)f);
}
}
}
Personnaly 在广播更改侦听器/更改处理程序的情况下,我倾向于将过程外部化到其他类,这使得可以正确使用参数类型并避免不安全的强制转换。如果您仍然愿意使用 reportChange() 从在 foo 对象中,这是一个可能的实现(否则您可以在 Broadcaster 中存储 T 引用)。
public class Broadcaster<T extends Foo> {
protected final List<ChangeHandler<? super T>> changeHandlers;
public Broadcaster() {
this.changeHandlers = new ArrayList<>();
}
public void startListeningTo(T obj) {// only T type objects are accepted
obj.registerBroadcaster(this);
}
public void addChangeHandler(ChangeHandler<? super T> changeHandler) {
changeHandlers.add(changeHandler);
}
void reportChange(Foo obj) {
T o = (T)obj;
for(ChangeHandler<? super T> c : changeHandlers) {
c.onChange(o);
}
}
}
public class Foo {
private final List<Broadcaster<?>> broadcasters = new ArrayList<>();
// cannot be accessed from outside of the package, only Broadcaster.startListeningTo(T o) can be used
final void registerBroadcaster(Broadcaster<?> b) {
broadcasters.add(b);
}
public final void reportChange() {
for (Broadcaster<?> b: broadcasters) {
b.reportChange(this);
}
}
}
public class Bar extends Foo {
// things happen in here that call super.reportChange();
}
public static void test() {
Broadcaster<Bar> broadcaster = new Broadcaster<>();
broadcaster.addChangeHandler(new ChangeHandler<Bar>() {
@Override
public void onChange(Bar obj) {
// do something
}
});
//note that you can use the same broadcaster for multiple objects.
Bar b = new Bar();
broadcaster.startListeningTo(b);
b.reportChange();
}
请注意,您将无法从 Bar 中添加 changeHandlers(但为自己注册 changeHandlers 真的是对象的工作吗?)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.