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?
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.
Although we cannot instantiate a generic array of a specific type parameter, we can pass an already created array to a generic class constructor.
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.
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.
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 anInteger[]
is also aNumber[]
, and you are free to pass or assign anInteger[]
where aNumber[]
is called for. (More formally, ifNumber
is a supertype ofInteger
, thenNumber[]
is a supertype ofInteger[]
.) You might think the same is true of generic types as well -- thatList<Number>
is a supertype ofList<Integer>
, and that you can pass aList<Integer>
where aList<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 aList<Number>
. Then the following code would allow you to put something that wasn't anInteger
into aList<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 aFloat
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With