当参数接受Collection <x <?>&gt; </x <?>时,不可能输入

时间:2013-08-15 18:48:14

标签: java generics types

此问题来自于javax.validation.ConstraintViolationException的构造函数的输入。它接受Set<ConstraintViolation<?>>作为参数。

虽然很容易得到一组ConstraintViolation&lt; X&gt;其中X是具体类型,似乎不可能获得一组“ConstraintViolation&lt;?&gt;”来自任何类型良好的API。如果不使用一些复杂的演员阵容,就不可能将前者转换为后者。 (转为Set<? extends ConstraintViolation<?>>然后转为Set<ConstraintViolation<?>>。)

那么你们认为API是错的还是我错了(以及为什么)?

2 个答案:

答案 0 :(得分:4)

API错了。除非实现需要向集合中添加新的ConstraintViolation<?>,否则它应该接受所有Set<? extends ConstraintViolation<?>>

这是一个展示为什么这更灵活的例子(由Paul Bellora提供,谢谢):

public class Main {

    interface Foo<T> { }

    interface SubFoo<T> extends Foo<T> { }

    static class Bar { }

    public static void main(String[] args) {

        Set<Foo<?>> arg1 = null;
        Set<SubFoo<?>> arg2 = null;
        Set<Foo<Bar>> arg3 = null;
        Set<SubFoo<Bar>> arg4 = null;

        Set<Foo<?>> inflexibleParam;
        inflexibleParam = arg1; //success
        inflexibleParam = arg2; //incompatible types
        inflexibleParam = arg3; //incompatible types
        inflexibleParam = arg4; //incompatible types

        Set<? extends Foo<?>> flexibleParam;
        flexibleParam = arg1; //success
        flexibleParam = arg2; //success
        flexibleParam = arg3; //success
        flexibleParam = arg4; //success
    }
}

ideone

答案 1 :(得分:2)

API错了。

理想情况下,我们想要接受协变泛型类型,我们应该使用? extends

一些通用声明本质上是协变的,这意味着它们应该始终与通配符一起使用,例如: Iterator。如果在没有通配符的情况下使用Iterator,那几乎肯定是错误的。

问题是,通配符语法是如此冗长,人们被关闭并经常忘记使用它。即使在核心库中,这也是广泛传播的,例如Iterable<T>.iterator()会返回Iterator<T>,但应返回Iterator<? extends T>

具有讽刺意味的是,擦除可以解决这个问题。我们知道Iterator<Apple>可以安全地用作Iterator<Fruit>静态,我们知道我们可以动态地将Iterator<Apple>强制转换为Iterator<Fruit>,这要归功于删除。所以,请继续进行粗暴演员。