//java architect /
any type of array, rather than writing
one method for String[], one for Person[],
and so on. Covariant arrays allow us to
write the following:
regardless of whether they are lists of
Bananas or Apples. As long as we do not
modify the lists, this should work without causing type errors.
LISTING 1
public class Main
{
COMMUNITY
public static void main(String[] args)
{
public static boolean find(Object
item, Object[] arr) { ... }
Object[] str = new String[ 10];
}
public static Object[] test2()
{
Of course, since Java SE 5, we have
a better solution. Using generics, the
method can be written as follows:
return new String[ 10];
}
}
JAVA IN ACTION
public static <T> boolean find(T
item, T[] arr) { ... }
ArrayList<? extends Fruit> f = new
ArrayList<Banana>();
f.add(new Banana());
// Prev. line causes compile error
Download all listings in this issue as text
This solution is not only guaranteed
to be type safe (that is, it will not cause
runtime exceptions because of a problem with the array’s type), but it has the
additional advantage of encoding the
constraint such that the type of the item
to find and the type of the array must
be compatible. However, this solution
works only in the presence of generics.
Before Java SE 5, using covariant arrays,
as shown above, was the only option
when implementing a method like this.
Unlike arrays, generics in Java are
invariant. This protects us from the runtime error in our earlier example involving arrays. For example, the following
code will not compile:
In Java, when we use generics with
wildcards, as shown above, we then cannot make modifications. For example,
in the code above, we can assign an
ArrayList<Banana> to the variable f of
type ArrayList<? extends Fruit>, but we
cannot then add any new items to f.
such annotation and is, thus, invariant.
Therefore, a Bowl<Banana> cannot be
assigned to a Bowl<Fruit>:
with additional complexity, despite the
fact that this can potentially cause problems at runtime.
Bowl[T]{} // invariant
CoBowl[+T]{ } // covariant as
// indicated by the +
val bowl : Bowl[Fruit] = new Bowl
[Banana]; // compile error
val coBowl : CoBowl[Fruit] = new
CoBowl[Banana]; // correct
Empirical Study of Covariant Arrays
Although the type problems caused by
covariant arrays in Java are well known,
there has been no study showing how
widely this problematic feature is used
by developers in practice. We conducted
an empirical study on the use of covariant arrays, aiming to determine how
frequently and in which
contexts they are used.
We analyzed 64 open
source programs from the
Qualitas Corpus, ranging
from games to system software and libraries. Although
the Qualitas Corpus contains
106 programs, we selected
only a subset, excluding
some of the programs that
needed to be fixed in order
to compile. A number of
ABOUT US
ArrayList<Fruit> f = new
ArrayList<Banana>();
blog
While this code is type safe, it somewhat limits us in terms of what we can
express. Sometimes we want to be able
to treat all lists of Fruit in the same way,
Covariance in Other Languages
Most languages support some form of
variance of types. Scala, for example,
allows developers to annotate a parametric type to indicate whether it
should be covariant, contravariant, or
invariant (that is, neither covariant nor
contravariant). This is called
definition-site variance.
In the following example, we annotate the parametric type CoBowl so that
it can be used covariantly (this is indicated by the + annotation). Assigning
a CoBowl<Banana> to CoBowl<Fruit>
is, therefore, allowed. The parametric
Bowl type, on the other hand, has no
In order to avoid type
errors like the one in our
original example, the mutable collections API in Scala
was designed to be invariant. However, the immutable
collections API can safely be
used covariantly.
Java supports
wildcards for
generics, which
allow us to specify
a bound to the
instance of a
generic.
45
ORACLE.COM/JAVAMAGAZINE /////////////////////////////////////////////// MAY/JUNE 2012