Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use arrays of generic types correctly?

Tags:

I have a class that maps incoming messages to matching readers based on the message's class. All message types implement the interface message. A reader registers at the mapper class, stating which message types it will be able to handle. This information needs to be stored in the message reader in some way and my approach was to set a private final array from the constructor.

Now, it seems I have some misunderstanding about generics and / or arrays, that I can't seem to figure out, see the code below. What is it?

public class HttpGetMessageReader implements IMessageReader {     // gives a warning because the type parameter is missing     // also, I actually want to be more restrictive than that     //      // private final Class[] _rgAccepted;      // works here, but see below     private final Class<? extends IMessage>[] _rgAccepted;      public HttpGetMessageReader()     {         // works here, but see above         // this._rgAccepted = new Class[1];          // gives the error "Can't create a generic array of Class<? extends IMessage>"         this._rgAccepted = new Class<? extends IMessage>[1];          this._rgAccepted[0] = HttpGetMessage.class;     } } 

ETA: As cletus correctly pointed out, the most basic googling shows that Java does not permit generic arrays. I definitely understand this for the examples given (like E[] arr = new E[8], where E is a type parameter of the surrounding class). But why is new Class[n] allowed? And what then is the "proper" (or at least, common) way to do this?

like image 392
Hanno Fietz Avatar asked May 22 '09 10:05

Hanno Fietz


People also ask

Can you use generics with an array?

To understand the reason, you first need to know two arrays are covariant and generics are invariant. Because of this fundamental reason, arrays and generics do not fit well with each other.

Can you create an array using a generic type parameter?

Although we cannot instantiate a generic array of a specific type parameter, we can pass an already created array to a generic class constructor.

What is the correct way of defining a generic method?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

Is array generic type?

An important difference between arrays and generics is how they enforce type checking. Specifically, arrays store and check type information at runtime. Generics, however, check for type errors at compile-time and don't have type information at runtime.


1 Answers

Java does not permit generic arrays. More information in the Java Generics FAQ.

To answer your question, just use a List (probably ArrayList) instead of an array.

Some more explanation can be found in Java theory and practice: Generics gotchas:

Generics are not covariant

While you might find it helpful to think of collections as being an abstraction of arrays, they have some special properties that collections do not. Arrays in the Java language are covariant -- which means that if Integer extends Number (which it does), then not only is an Integer also a Number, but an Integer[] is also a Number[], and you are free to pass or assign an Integer[] where a Number[] is called for. (More formally, if Number is a supertype of Integer, then Number[] is a supertype of Integer[].) You might think the same is true of generic types as well -- that List<Number> is a supertype of List<Integer>, and that you can pass a List<Integer> where a List<Number> is expected. Unfortunately, it doesn't work that way.

It turns out there's a good reason it doesn't work that way: It would break the type safety generics were supposed to provide. Imagine you could assign a List<Integer> to a List<Number>. Then the following code would allow you to put something that wasn't an Integer into a List<Integer>:

List<Integer> li = new ArrayList<Integer>(); List<Number> ln = li; // illegal ln.add(new Float(3.1415)); 

Because ln is a List<Number>, adding a Float to it seems perfectly legal. But if ln were aliased with li, then it would break the type-safety promise implicit in the definition of li -- that it is a list of integers, which is why generic types cannot be covariant.

like image 159
cletus Avatar answered Oct 04 '22 08:10

cletus