2017-04-10 57 views
0

我对泛型有点不知所措。我有以下代码:为什么Java编译器不让我返回一个泛型?

public interface SampleValue<T> { 
    T getValue(); 
} 

public static class SampleBoolean implements SampleValue<Boolean> { 
    @Override 
    public Boolean getValue() { 
     return Boolean.TRUE; 
    } 
} 

public static final class SampleValueGenerator { 
    private SampleValueGenerator() { 
     // do not call 
    } 

    public static <T, R extends SampleValue<T>> R forType(Class<T> clazz) { 
     if(Boolean.class.equals(clazz)) { 
      return new SampleBoolean(); 
     } 
    } 
} 

当我试试这个,的IntelliJ(即编译)告诉我,RSampleBoolean是不兼容的类型(用于return线)。

当我尝试的非通用(RAW)返回类型

public static <T> SampleValue forType(Class<T> clazz) {

我没有得到任何错误;

public static <T, R extends SampleValue<?>> R forType(Class<T> clazz) {

(与?通配符),但是再次失败。而对于

public static <T> SampleValue<T> forType(Class<T> clazz) {

我得到Incompatible types, Found: SampleBoolean, Required: SampleValue<T>

我的猜测是它与例如名单不是列表的祖先(但是兄弟姐妹),但我没有看到具有上述树木的木材。

有人可以请解释发生了什么,以及为什么长示例不起作用?

更新:注:当时的想法是有一些更多的if/else对于不同类型的分支,但我停在编译时就开始抱怨起来......

+1

示例代码可以通过声明一个'AnotherEampleSampleBoolean实现SampleValue {...}',然后调用'AnotherSampleBoolean result = forType(Boolean.class)'来挫败。 'R'会被推断为'AnotherSampleBoolean',并且在运行时会抛出'ClassCastException'将'SampleBoolean'转换为'AnotherSampleBoolean'。所以,如果你打算用例如未经检查的铸造过去的编译器错误,声明' SampleValue forType(类)'是最稳定的。 – Radiodef

+0

@Radiodef使某种意义上说,但' SampleValue forType(类)'失败,太... – Christian

+0

'公共静态 SampleValue forType(类 clazz所){'(使用通配符,非通用返回类型)的作品... – Christian

回答

0

要退回R是永远的R实现Boolean的SampleValue,而不是实现T的SampleValue的R(在运行时设置的泛型类型)。

// if i do this 
SampleValueGenerator.forType(Integer.class) 
// i am expecting something that implements SampleValue<Integer> 
// but you are always returning something that implements SampleValue<Boolean> 

编辑这应该工作(并没有测试)

public static <T, R extends SampleValue<T>> R forType(Class<T> clazz) { 
    return() -> { 
     try{ 
      return (T)clazz.newInstance(); // Also clazz should have a default constructor. 
     }catch(Excepetion e){ 
      // This catch block should be for NoSuchMethodException and InstantionException 
     } 
    } 
} 
1

我认为这个问题是SampleBoolean实现SampleValue<Boolean>这是一个特定的类型,不是通用的。另一方面,R被声明为扩展一个通用类型SampleValue<T>

SampleValue<T>SampleValue<Boolean>是两种不同的类型,所以这就是为什么你得到了编译错误。该forType函数要返回一个泛型类型R和你返回一个特定的类型用下面的语句:

return new SampleBoolean(); 
2

的原因是,您的条件并不能证明什么编译器。

您在这里的混乱涉及您的条件:

if(Boolean.class.equals(clazz)) 

有了这个检查,你推断TBoolean,但是编译器没有执行这一方式。编译器并不隐含假设此检查将确保TBoolean。(所有的编译器知道在这种方法的情况下约equals的是,它返回一个boolean。)

因此,尽管你的支票,RSampleBoolean是不兼容的类型,因为R extends SampleValue<T>T可以任何东西。

我真的不能想出一种方法来确保返回new SampleValue<T>基于T,但如果我这样做,我会用解决方案编辑此答案。我很想看到其他人的想法。

+0

他的界面是一个函数接口,可能可以使用lambda表达式,然后通过反射使用clazz.newInstance()创建一个类型T,唯一的缺点是如果没有默认的构造函数和异常需要处理。 –

相关问题