我有以下方法:
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 );
您不能执行此操作,因为编译器无法确认LoginResult
的类型为T
,因为它是在调用站点推断的(即调用者决定将使用哪种类型的参数)。
要回答您编辑的问题,
如果没有明确的演员阵容,就无法做到这一点。因此,最简单(但残酷)的解决方案是:
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();
稍微扩展一下SimonC的答案。
考虑你有类:
Result
LoginResult extends Result
OtherResult extends Result
如果您的类型T是OtherResult
,那么尝试返回LoginResult是无稽之谈。编译器只有在编译时能够保证敏感度的情况下才会编译它。就目前情况来看,它不能,因为T可能是与LoginResult不兼容的类型。
返回类型为<T extends Result>
并不意味着您必须返回属于Result
的内容。这意味着您需要返回T
,但T
必须是Result
的子类
关于您的编辑
Edit:我想使用泛型的原因是这样我就可以做一些事情以下内容:
Command<LoginResult> login = new Command<>(); LoginResult result = execute( login );
我不确定execute到底应该做什么,但我的第一个想法是让execute成为Command的一个实例方法。
然后你会有
public T execute()
{
}
问题是您需要一种实例化LoginResult
的方法。这就是我们需要更多关于您具体问题的信息以给出详细答案的地方。
我会在Result中创建一个名为newInstance的静态方法。既然知道T
是结果的某个子类,就可以调用T.newInstance()
。然后,loginResult的潜在构造函数可以是私有的,您可以通过它的newInstance方法调用它。
这需要将您的命令定义为:
public class Command<T extends Result>
Result必须有一个带有签名的方法:
public static Result newInstance()
可能出现的另一个问题是,您不希望将"命令"约束为"结果"。没关系,你可以创建一个新类:
public class ResultCommand<T extends Result> extends Command
比未检查的强制转换更安全的解决方案:
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
参数创建结果,那么您需要另一个可以负责类型检查的参数。
编译器需要知道返回值将匹配类型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 );
请注意这里的冗余信息,但这是必需的,因为泛型在编译中会丢失。