2013-08-15 10 views
6

Ten problem pochodzi z wpisania konstruktora javax.validation.ConstraintViolationException. Przyjmuje on argument jako Set<ConstraintViolation<?>>.Niemożliwe wpisanie, gdy argument akceptuje kolekcję <X<?>>

Mimo że bardzo łatwo uzyskać zestaw ConstraintViolation < X> gdzie X jest konkretnym typem, wydaje się niemożliwe uzyskanie zestawu "ConstraintViolation <?>" Z dowolnego dobrze napisanego API. I nie można zamienić tego pierwszego na drugie bez użycia zawiłych obsad. (Przesyłanie do Set<? extends ConstraintViolation<?>>, a następnie do Set<ConstraintViolation<?>>.)

Czy uważasz, że interfejs API jest nieprawidłowy lub nie mam racji (i dlaczego)?

+3

Dlaczego sądzisz, że niemożliwe jest uzyskanie 'Set >? Czy możesz pokazać konkretny przykład, w którym utknąłeś w tym wydaniu? –

+0

Wygląda na to, że konstruktor powinien zająć 'Set > '. –

+2

Myślę, że jest to duplikat http://stackoverflow.com/questions/12096846/how-do-i-construct-a-constraintviolationexception – Katona

Odpowiedz

4

Interfejs API jest nieprawidłowy. O ile implementacje nie wymagają dodania nowego zestawu ConstraintViolation<?> s, powinien on akceptować wszystkie Set<? extends ConstraintViolation<?>>.

Oto przykład wykazanie, dlaczego jest to bardziej elastyczne (dostarczone przez Pawła Bellora dzięki):

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

Huh? http://ideone.com/5xeuqp –

+0

@OliCharlesworth Myślę, że źle zrozumiałeś odpowiedź. –

+0

Chyba można przekazać dowolny typ parametryzowany do 'ConstraintViolation '. –

2

API jest źle.

Idealnie wszędzie, gdzie chcemy zaakceptować typ kowariantny, powinniśmy użyć ? extends.

Niektóre ogólne deklaracje są z natury kowariancyjne, co oznacza, że ​​powinny zawsze być używane z symbolem wieloznacznym, np. Iterator. Jeśli używasz Iterator bez symbolu wieloznacznego, prawie na pewno jest on nieprawidłowy.

Problem polega na tym, że składnia wieloznaczna jest tak dokładna, że ​​ludzie są wyłączeni i często zapomnieli jej użyć. Jest to szeroko rozpowszechnione nawet w podstawowych bibliotekach, np. Iterable<T>.iterator() zwraca Iterator<T>, a powinien zwrócić Iterator<? extends T>.

Erasure, ironicznie, może uratować ten problem. Wiemy, że Iterator<Apple> może być bezpiecznie użyty jako statycznie i i możemy brutalnie rzucać Iterator<Apple> do Iterator<Fruit> dynamicznie, dzięki skasowaniu. Więc idź dalej i zrób brutalny rzut.

+0

+1 Ale zwróć uwagę, że generalnie niepotrzebne jest * zwracanie * typów ogólnych za pomocą '? extends' choć - parametry metod są tam, gdzie mogą zwiększyć elastyczność. –

+0

Przydaje się implementacja podklasy, aby zwrócić 'Iterator ' jako 'Iterator '. – ZhongYu

+0

Dlaczego? Czy to nie usuwa informacji? –

Powiązane problemy