Например:
- Тип Optional<A> ковариантен. То есть, например, всегда можно привести Optional<Integer> к Optional<Number> и это будет безопасно.
- Тип Consumer<A> контравариантен. Это значит, что всегда можно безопасно привести Consumer<Number> к Consumer<Integer>.
- Наконец, тип List<A> инвариантен, так как он не является ни ковариантным, ни контравариантным . То есть нельзя безопасно привести не от List<Integer> к List<Number> и не от List<Number> к List<Integer>
- Если вам нужно только читать из списка, то вы объявляете тип переменной как ковариантный: List<? extends Number>.
- Если вам нужно только писать в список, то вы объявляете тип переменной как контравариантный: List<? super Number>.
- Наконец, если вам нужно и читать из списка, и писать в него, то вы пишете просто List<Number>, то есть объявляете тип переменной как инвариантный.
В этом посте я предлагаю альтернативу wildcard'ам. Конечно, лучше бы если бы в Java, наконец, ввели declaration site variance на уровне языка, т.е. возможность указать ковариантность/контравариантность типа во время его объявления (Optional<out A> / Consumer<in A>), и я не понимаю, почему это до сих пор не сделали.
Итак, правило следующее. Если вы объявляете ковариантный тип, то вы добавляете метод widen() со следующей сигнатурой:
public class Optional<A> {
...
public static <B, A extends B> Optional<B> widen(Optional<A> opt) {
return (Optional) opt;
}
}
Теперь всегда, когда вам нужно привести Optional<Integer> Optional<Number> вы пишите:
Optional<Integer> opt = ...;
Optional<Number> opt2 = Optional.widen(opt);
И всё, никаких wildcard'ов.
Аналогично для контравариантных типов вы добавляете метод narrow():
public interface Consumer<A> {
...
public static <A, B extends A> Consumer<B> narrow(Consumer<A> con) {
return (Consumer) con;
}
}
Теперь если нужно привести Consumer<Number> к Consumer<Integer>, вы пишите:
Consumer<Number> con = ...;
Consumer<Integer> con2 = Consumer.narrow(con);
По-моему, такой подход делает код намного чище. Я понимаю, что мы заменили одно уродство на другое, но я думаю, что моё уродство является менее страшным.