我有以下方法:
public <T extends Result> T execute(Command<T> command)
{
return new LoginResult();
}
这里,Result
是一个接口,类LoginResult
确实实现了这个接口。但是,我收到了错误:
不兼容的类型,必需: T ,找到: com.foo.LoginResult
然而,如果我将方法签名更改为:
public Result execute(Command<T> command)
然后相同的返回行正常工作,没有任何错误。
这里有什么问题?如何从此方法返回LoginResult
?
编辑:我想使用泛型的原因是,我可以做如下的事情:
Command<LoginResult> login = new Command<>();
LoginResult result = execute( login );
答案 0 :(得分:16)
您无法执行此操作,因为编译器无法确认LoginResult
的类型为T
,因为它是在调用站点推断的(即调用方决定将使用哪个类型参数)
答案 1 :(得分:6)
要回答您编辑的问题,
如果没有明确的演员表,没有办法做到这一点。所以最简单(但野蛮)的解决方案是:public <T extends Result> T execute(Command<T> command) {
return (T) new LoginResult();
}
但是这样你就完全负责为正确的命令实例化正确的结果,因为编译器不再帮助你了。
唯一可以帮助您动态实例化的东西就是对实际Class<T>
的引用。
因此,如果您在命令中添加Class<T> getResultType()
之类的方法,则可以编写:
return command.getResultType().newInstance(); // instead of new SpecificResult()
这当然意味着每个Result
实现中都有一个默认构造函数,等等......
更友好的OO方法(无反射)是让命令实例化自己的结果(使用工厂方法T instantiateResult()
):
return command.instantiateResult();
答案 2 :(得分:2)
扩展SimonC的答案。
考虑你有课程:
Result
LoginResult extends Result
OtherResult extends Result
如果您的类型T是OtherResult
,那么尝试返回LoginResult是无稽之谈。只有在编译时它可以保证敏感性时,编译器才会编译它。因为它不能,因为T可能是与LoginResult不兼容的类型。
返回类型<T extends Result>
并不意味着您必须返回Result
的内容。这意味着您需要返回T
的内容,但T
必须是Result
关于你的编辑
编辑:我想使用泛型的原因是,我可以做类似的事情 以下内容:
Command<LoginResult> login = new Command<>(); LoginResult result = execute( login );
我不确定应该执行什么操作,但我首先想到的是执行Command的实例方法。
然后你会
public T execute()
{
}
问题是您需要一种实例化LoginResult
的方法。这是我们需要更多信息来解决您的具体问题,以提供详细的答案。
我会在Result中创建一个名为newInstance的静态方法。然后,因为您知道T
是结果的一些子类,您可以调用T.newInstance()
。那么你的loginResult的可能构造函数可以是私有的,你可以通过它的newInstance方法调用它。
这需要将您的命令定义为:
public class Command<T extends Result>
结果必须有签名方法:
public static Result newInstance()
可能出现的另一个问题是您不希望将Command限制为结果。没关系,你可以上新课:
public class ResultCommand<T extends Result> extends Command
答案 3 :(得分:1)
比未经检查的演员更安全的解决方案:
public <T extends Result> T execute(Command<T> command, Class<T> cls)
{
return cls.cast(new LoginResult());
}
LoginResult result = execute(loginCommand, LoginResult.class);
同时拥有这两个参数的理由是execute
的合同隐含为:
Command
的方法获取类型T
的结果以将其返回。T
。使用Java的泛型,只有<T> T foo(Bar<T>, Baz<T>)
形式的泛型方法的参数才能安全地“创建”T
类型的对象。因此,如果在您的情况下,您没有使用command
参数创建结果,则需要另一个可以处理类型检查的参数。
答案 4 :(得分:-1)
编译器需要知道返回值将匹配类型T,但它不能。 如果您将Command作为参数传递它将匹配,但如果您传递Command则它可能不匹配,因为T将被定义为其他类型。
您可以在运行时强制转换它,如果您返回的对象与T不匹配,它将抛出ClassCastException。
public <T extends Result> T execute(Command<T> command, Class<T> type)
{
return type.cast( new LoginResult() );
}
你会这样称呼它:
Command<LoginResult> login = new Command<>();
LoginResult result = execute( login, LoginResult.class );
请注意这里的冗余信息,但由于编译中遗漏了泛型,因此需要它。