通用参数多态性中的差异

时间:2015-08-28 23:01:38

标签: java generics

public class Foo<T extends Bar>{
    private Class<T> _type;
    public Foo( Class<T> _type ){
        this._type = _type;
    }
    public Collection<T> hypothetical( List<T> items ){ //PROBLEMATIC
        return dostuffWithItems( items );
    }
}

用法:

Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
List<ChildBar> items = ( List<ChildBar> ) foo.hypothetical( new ArrayList<ChildBar>() ); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>)

编译器会接受
List<ChildBar> items投射List<?>参数
或将hypothetical( List<T> items )签名更改为
a)hypothetical( List<ChildBar> items )
b)hypothetical( List<? extends Bar> items )

但是,没有一个替代方案可以确保假设方法的List items参数T type是Foo类T参数类型的等效运行时类型。我目前正在使用额外的方法来验证参数类型。 Java泛型构造中是否有更好的方法可以自动实现这一点,而无需额外的逻辑?或者更好的是,为什么我不能将foo声明为Foo<? extends Bar>然后在运行时填写实际的类型参数?

3 个答案:

答案 0 :(得分:5)

我编辑了你的代码并添加了缺少的东西以使其可编辑,我可以确认唯一有问题的部分是:

  • 缺少dostuffWithItems方法。
  • 使用hypothetical方法名称的拼写错误。
  • Collection<ChildBar>分配给List<ChildBar>

前两个很容易修复。

最后一个要求您更改API方法的更改,或更改您调用它的代码。这些都不是(IMO)有问题的。此外,

值得注意的是,如果类型是非泛型的,您将获得所有这些错误。您无法在没有类型转换的情况下将Collection分配给List

这是我的代码供你玩。 (复制并粘贴到适当命名的文件中......)

public class Bar {
}

public class ChildBar extends Bar {
}

import java.util.*;

public class Foo<T extends Bar> {
    private Class<T> _type;
    public Foo( Class<T> _type ) {
        this._type = _type;
    }
    public Collection<T> hypothetical( List<T> items ) { 
        return items;  // dummy implementation ...
    }
}

import java.util.*;

public class Main {
    public static void main(String[] args) {
       Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
       Collection<ChildBar> items = 
               foo.hypothetical( new ArrayList<ChildBar>() );
    }
}

答案 1 :(得分:2)

接受的答案并没有准确解释为什么问题中的代码段(编辑后)会被编译器拒绝。

我们从观察到@Stephen C的答案的片段被接受开始,而问题的第8版被拒绝。区别在于:在后一版本中,变量foo使用通配符参数化类型Foo<? extends ChildBar>声明,而Stephen C从早期版本复制Foo<ChildBar>(我们似乎都同意这是解决编译错误的合适方法。)

要了解为什么这种差异至关重要,请参阅Foo<? extends ChildBar> foo这个通配符作为捕获传播到foo.hypothetical的调用签名,因此此调用呈现为hypothetical(List<capture#2-of ?>),意味着该参数具有未知(上限)类型参数。 List<ChildBar>与该类型不兼容,因此编译错误。

还要注意所有提到&#34;运行时&#34;在这个线程中是不合适的,所有这些都是在编译时静态解决的。也许你的意思是调用类型或实际参数的类型,而不是声明的类型(形式参数)。编译器不知道实际的运行时类型。

答案 2 :(得分:-2)

这在Java中似乎是不可能的。
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
这使得foo具有模糊的参数类型。很明显,ChildBar将成为真正的事实上的参数类型。使用foo.hypothetical()调用List<ChildBar>方法会使此假设不真实。虽然foo.hypothetical只接受包含foo参数类型元素的List<>参数,但它仍然无法识别该参数是ChildBar对象的列表。

对于此用例,必须在foo声明期间指定对象参数类型,以使其成为foo运行时引用的一部分。
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
现在,foo.hypothetical方法的所有符合List<ChildBar>参数都将被正确接受为foo声明的参数类型的元素。