Java Generics返回类型问题

时间:2014-02-11 14:51:39

标签: java generics

我有以下方法:

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 );

5 个答案:

答案 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 );

请注意这里的冗余信息,但由于编译中遗漏了泛型,因此需要它。