Java泛型方法参数传递问题



我有一个关于传递参数给泛型方法的问题。代码如下:

public class View<T extends View<T,PM>, PM extends Source>  {
    protected PM source;
    protected EventManager<T, PM> eventManager;
    public View(PM s){
        this.source = s;
        eventManager = new EventManager<T, PM>();
        eventManager.setTarget(this); //error: "The method setTarget(T) in the type
                              //EventManager<T,PM> is not applicable for the arguments (View<T,PM>)"
        eventManager.setSource(s);
    }
    public void setBinding(Topic topic, IEventAction<T,PM> action){
        eventManager.setEventAction(topic, action)
    }
}
/** 
* EventManager class has to remain completely generic. The type parameters cannot "extends"
* anything because the EventManager is used also in other parts where T and S will have to be
* classes other than "View" and "Source"
*/
public class EventManager<T, S> {
    protected T target;
    protected S source;
    private LinkedHashMap<Topic, IEventAction<T, S>> eventActions;
    public EventManager(T target, S source){
        this.target = target;
        this.source = source;
    }
    public void setTarget(T target){
        this.target = target;
    }
    public void setSource(S source){
        this.source = source;
    }
    public void setEventAction(Topic topic, IEventAction<T, S> action) {
        //some code here ...
        omissis...
        eventActions.put(topic, action);
        omissis...
    }
    //other methods down here...omissis
}

Eclipse给出了我在"eventManager.setTarget(this);"行注释中的错误。我不明白为什么它会给我这个错误。无论如何,我找到了一个解决方案(显然),但我不确定我做的是"干净"还是"肮脏"的事情。解决方案如下:

 eventManager.setTarget((T)this);

但是它给了我一个警告:"类型安全:从视图到T的未检查强制转换"。为了消除警告,我还将以下内容放在构造函数方法的顶部:

@SuppressWarnings("unchecked")

它似乎工作,但有什么问题?你是否有其他"更干净"的解决方案(如果存在的话)?你认为这是一种"肮脏"的方法吗?

发生错误是因为您正在使用T实例化EventManager,它可以是运行时View的任何子类,但您正在传递View(在编译时已知)。由于通常在需要子类时不能传递超类,因此代码无法编译。

解决方案(不改变代码)当然是将超类强制转换为子类(这就是你正在做的),并尽量不要得到ClassCastException。

如果你确信你永远不会传递不兼容的类型,那么我想这是可以的(尽管非常令人困惑)。或许可以重新设计一下

public class View<T extends View<T,PM>, PM extends Source>  {

您似乎希望T是"this的类型"。但这在语言中没有表达。

一般来说,这里的方法是使类抽象,并添加一个抽象的getThis方法:

public abstract class View<THIS extends View<THIS,PM>, PM extends Source>  {
    protected abstract THIS getThis();
    ...
        eventManager.setTarget(getThis());
        ...
public final class SomeView extends View<SomeView,SomeSource> {
    protected SomeView getThis() {
        return this;
    }
    ...

这个错误是因为事件管理器在构造过程中被初始化为持有类型的"T",但是你试图分配一个" View "的实例(T扩展View,使View成为超类),这是不对的。

我同意Tudor所说的,但最好不要对方法/构造函数抑制未检查的警告。这将导致隐藏任何其他未检查的异常。我建议你做你需要的那条线。示例如下:

@SuppressWarnings("unchecked")
T t = (T)this;
eventManager.setTarget(t);

我认为遵守声明EventManager应该扩展视图,因为在线eventManager = new EventManager<T, PM>();上的T被定义为T extends View<T,PM> -命名类型模板相同使其混淆

我不知道你到底在干什么,但是我有件事。

解决方案1

public class View<PM extends Source>  {
    protected PM source;
    protected EventManager<View<PM>, PM> eventManager;
    public View(PM s){
        this.source = s;
        eventManager = new EventManager<>(); // diamond notation (Java 7 only)
        eventManager.setTarget(this); 
        eventManager.setSource(s);
    }
}    

你仍然可以有ViewsetTarget的子类将总是接收正确的预期类型。

解决方案2:

EventManager应该只采用Source(或子类)作为源,不是吗?

public class View {
    protected Source source;
    protected EventManager<View> eventManager;
    public View(Source s){
        this.source = s;
        eventManager = new EventManager<>(); // diamond notation (Java 7 only)
        eventManager.setTarget(this); 
        eventManager.setSource(s);
    }
}    
public class EventManager<T> {
   protected T target;
   protected Source source;
   public void setTarget(T t) ...
   public void setSource(Source s) ...
}
解决方案

3

你应该有一个Target类或接口,这样EventManager根本不会被参数化,View将扩展或实现Target。这样就没有泛型了。如果简单继承就可以完成任务,那么就不需要泛型了。

注意:正如Scorpion提到的,你不应该在构造函数中转义this;你可能会得到一些事件,触发访问View之前,它还没有完全构建。

最新更新