Dato il seguente esempio (usando JUnit con matchers Hamcrest):Quando i generici Java richiedono anziché <T> e c'è qualche svantaggio della commutazione?
Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));
Questo non viene compilato con il metodo JUnit assertThat
firma:
public static <T> void assertThat(T actual, Matcher<T> matcher)
Il messaggio di errore del compilatore è:
Error:Error:line (102)cannot find symbol method
assertThat(java.util.Map<java.lang.String,java.lang.Class<java.util.Date>>,
org.hamcrest.Matcher<java.util.Map<java.lang.String,java.lang.Class
<? extends java.io.Serializable>>>)
Tuttavia, se cambio la firma del metodo assertThat
in:
public static <T> void assertThat(T result, Matcher<? extends T> matcher)
Quindi la compilazione funziona.
Quindi tre domande:
- Perché proprio non ha la versione corrente compilare? Anche se capisco vagamente i problemi di covarianza qui, non potrei certamente spiegarlo se dovessi farlo.
- C'è qualche svantaggio nel cambiare il metodo
assertThat
inMatcher<? extends T>
? Ci sono altri casi che si interrompono se lo fai? - C'è qualche punto per la generalizzazione del metodo
assertThat
in JUnit? La classeMatcher
non sembra averlo richiesto, dal momento che JUnit chiama il metodo di corrispondenza, che non è stato digitato con alcun generico, e sembra proprio un tentativo di forzare un tipo di sicurezza che non fa nulla, poiché loMatcher
non sarà sufficiente in effetti corrispondono, e il test fallirà a prescindere. Non sono coinvolte operazioni pericolose (o almeno così sembra).
Per riferimento, ecco l'implementazione di JUnit assertThat
:
public static <T> void assertThat(T actual, Matcher<T> matcher) {
assertThat("", actual, matcher);
}
public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) {
if (!matcher.matches(actual)) {
Description description = new StringDescription();
description.appendText(reason);
description.appendText("\nExpected: ");
matcher.describeTo(description);
description
.appendText("\n got: ")
.appendValue(actual)
.appendText("\n");
throw new java.lang.AssertionError(description.toString());
}
}
questo collegamento è molto utile (generici, ereditarietà e sottotipo): http://docs.oracle.com/javase/tutorial/java/generics/inheritance.html –