此问题来自于javax.validation.ConstraintViolationException
的构造函数的输入。它接受Set<ConstraintViolation<?>>
作为参数。
虽然很容易得到一组ConstraintViolation&lt; X&gt;其中X是具体类型,似乎不可能获得一组“ConstraintViolation&lt;?&gt;”来自任何类型良好的API。如果不使用一些复杂的演员阵容,就不可能将前者转换为后者。 (转为Set<? extends ConstraintViolation<?>>
然后转为Set<ConstraintViolation<?>>
。)
那么你们认为API是错的还是我错了(以及为什么)?
答案 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>
,这要归功于删除。所以,请继续进行粗暴演员。