我有一个关于将参数传递给泛型方法的问题。代码如下:
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);
但是它给了我一个警告:“类型安全:从View查看到T的未选中”。为了消除警告,我还将以下内容置于构造函数方法之上:
@SuppressWarnings("unchecked")
看起来有效,但出了什么问题?你有另一个“更清洁”的解决方案(如果存在)?你认为这是一种“肮脏”的做法吗?
非常欢迎任何消息。
答案 0 :(得分:4)
发生错误的原因是您使用T实例化EventManager,它可以是运行时View的任何子类,但您正在传递完全View(在编译时已知)。因为通常在需要子类时不能传递超类,所以代码不能编译。
解决方案(不更改代码)当然是将超类强制转换为子类(这就是你正在做的事情),并尽量不要获得ClassCastException。
如果您确定永远不会传递不兼容的类型,那么我猜(虽然非常令人困惑)。也许试着以某种方式重新设计它。
答案 1 :(得分:3)
在第
行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;
}
...
答案 2 :(得分:1)
错误是因为事件管理器在构造期间被初始化为保持一种“T”类型,但是您正在尝试分配一个“View”实例(T扩展View,使View成为超类),这是不对的
答案 3 :(得分:0)
我同意Tudor所说的内容,但最好不要为方法/构造函数抑制未经检查的警告。这将导致隐藏任何其他未经检查的异常。我建议你做你需要的线路。以下是示例。
@SuppressWarnings("unchecked")
T t = (T)this;
eventManager.setTarget(t);
答案 4 :(得分:0)
我认为遵守声明EventManager应该扩展View,因为行eventManager = new EventManager<T, PM>();
上的T被定义为T extends View<T,PM>
- 命名类型模板同样会让人感到困惑。
答案 5 :(得分:0)
我不知道你在做什么,但这里有什么。
解决方案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);
}
}
您仍然可以拥有View
的子类,而setTarget
始终会收到正确的预期类型。
解决方案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
的访问。