Question

The following code is from Effective Java book :

Set<Integer> integers = ... ;
Set<Double> doubles = ... ;
Set<Number> numbers = union(integers, doubles);

This code didn't compile and the author suggests to get around this problem by telling the compiler the exact type like the following :

Set<Number> numbers = Union.<Number>union(integers, doubles)

If the signature of the union is as follows why would the earlier program not compile ?Also what is this particular workaround idiom called ?

public static <E> Set<E> union(Set<? extends E> s1,
Set<? extends E> s2)
Was it helpful?

Solution

Notice that Double and Integer not only extends Number but also implements Comparable. So return type guessed by compiler would be Set<Number&Comparable> which cannot be cast to Set<Number>. You need to tell compiler which one of follow type to use. With follow code you do not need a exact type.

interface X {}
class U implements X {}
class V implements X {}

public static void main(String[] args) {
    Set<U> integers = new HashSet<U>();
    Set<V> doubles = new HashSet<V>();
    Set<X> numbers = union(integers, doubles);
}
public static <E> Set<E> union(Set<? extends E> s1,   Set<? extends E> s2) {
    return null;

}

But if you change it a bit you will get origin error.

   interface X {}
interface Y {}
class U implements X, Y {}
class V implements X, Y {}

public static void main(String[] args) {
    Set<U> integers = new HashSet<U>();
    Set<V> doubles = new HashSet<V>();
    Set<X> numbers = union(integers, doubles);
}
public static <E> Set<E> union(Set<? extends E> s1,   Set<? extends E> s2) {
    return null;

}

OTHER TIPS

The only issue is that the compiler isn't smart enough to figure out what type to substitute for E, so you have to specify it explicitly. There isn't a name for that idiom, it's just how you explicitly specify generic type parameters.

If the union method is defined as <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2), older Java compilers aren't smart enough to correctly infer the type of the return. Thus it is necessary for the compiler to be told which type you want back from the method, to ensure type safety. (I think that Java 7 compilers might be able to infer this correctly, but I am not sure).

I also do not know of a name for this 'idiom', it is just called a generic function.

The Java compiler attempts to narrow the return type as much as it can. After attempting to mock up this example, I get the following compiler error message without specifying .<Number>union:

EffectiveJava.java:19: incompatible types
found   : java.util.Set<java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>>
required: java.util.Set<java.lang.Number>
      Set<Number> numbers = union(integers, doubles);

It attempts to include Comparable in "E" because both Integers and Doubles are also Comparable. So that is why you have to tell the compiler, no, I just want Number with .<Number>union.

To my knowledge, I don't know if there is a name for the idiom.

When you say

Set numbers = Union.union(integers, doubles) 

Here Union is the name of class containing the method static method union

static  Set union(Set s1, Set s2) 

so if you class name is GenericDemo where the method union is defined, the code you will write-

Set numbers = GenericDemo.union(integers, doubles)

you can replace GenericDemo by the object instance, if the union is non static method.

Its all about generics notations principles and concepts. I am taking forward from example posted by ijrandom. Let us take the first scenario where U and V implements only X. For the time being we are not assigning method output to a reference variable. We have called the union method as given below:

union(integers, doubles);

We know that for generics the actual parameter type is determined by the type you specify while calling method, so in this case the return type for method will be Set of X, just move cursor over the method call you will see how java compiler has determined actual parameter type at compile time.

In our second scenario we once again call the method union:

union(integers, doubles);//Note we have not yet assigned the returned value to a variable.

Move cursor over method call, you will notice that return type for method changes to Set of unknown extends X, thus here the compiler is not able to resolve completely the actual type as U and V implements both X and Y and X is not Y. Hence as soon as you assign value to variable, error is produced as compiler is still not able to identify the actual type. In such scenario you need to tell compiler which actual type to use.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top