What variance is imposed on generic type parameters? How much control does Java give you over this?

Solution:

Java’s generic type parameters are invariant . This means for any distinct types A and B, G<A> is not a subtype or supertype of G<B>. As a real world example, List<String> is not a supertype or subtype of List<Object>. So even though String extends (i.e. is a subtype of) Object, both of the following assignments will fail to compile:

        List<String> strings = Arrays.<Object>asList("hi there");
        List<Object> objects = Arrays.<String>asList("hi there");

Java does give you some control over this in the form of use-site variance . On individual methods, we can use ? extends Type to create a covariant parameter. Here’s an example:

        public double sum(List<? extends Number> numbers) {
            double sum = 0;
            for (Number number : numbers) {
                sum += number.doubleValue();
            }
            return sum;
        }


        List<Long> longs = Arrays.asList(42L, 128L, -10L);
        double sumOfLongs = sum(longs);

Even though longs is a List<Long> and not List<Number>, it can be passed to sum.

Similarly, ? super Type lets a method parameter be contravariant . Consider a function with a callback parameter:

        public void forEachNumber(Callback<? super Number> callback) {
            callback.call(50.0f);
            callback.call(123123);
            callback.call((short) 99);
        }

forEachNumber<span> </span>allows Callback<Object> to be a subtype of Callback <Number>, which means any callback that handles a supertype of Number will do:

        forEachNumber(new Callback<Object>() {
            @Override public void call(Object value) {
                System.out.println(value);
            }
        });

Note, however, that attempting to provide a callback that handles only Long (a subtype of Number) will rightly fail:

        // fails to compile!
        forEachNumber(new Callback<Long>() { ... });

Liberal application of use-site variance can prevent many of the unsafe casts that often appear in Java code and is crucial when designing interfaces used by multiple developers.

0 answers