6

这个问题来自于构造函数的类型javax.validation.ConstraintViolationException。它接受Set<ConstraintViolation<?>>作为参数。

虽然很容易获得一组 ConstraintViolation<X>,其中 X 是一个具体类型,但似乎不可能从任何类型良好的 API 获得一组“ConstraintViolation<?>”。如果不使用一些令人费解的演员表,就不可能将前者转换为后者。(投射到Set<? extends ConstraintViolation<?>>然后到Set<ConstraintViolation<?>>。)

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

4

2 回答 2

4

API 是错误的。除非实现需要向ConstraintViolation<?>集合中添加 new ,否则它应该接受 all 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
    }
}

想法

于 2013-08-15T18:52:49.503 回答
2

API 是错误的。

理想情况下,任何我们想要接受协变泛型类型的地方,我们都应该使用? extends.

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

问题是,通配符语法非常冗长,人们被关闭并且经常忘记使用它。这甚至在核心库中也很普遍,例如Iterable<T>.iterator()返回Iterator<T>,而它应该返回Iterator<? extends T>

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

于 2013-08-15T19:35:08.827 回答