This week's book giveaway is in the OCAJP 8 forum. We're giving away four copies of OCA Java SE 8 Programmer I Study Guide and have Edward Finegan & Robert Liguori on-line! See this thread for details.
I have an interface that specifies an encoder in a generic way. It specifies a method to convert an object of type T, resulting in a byte array:
I have several implementations of this, for example:
Now I have a heterogenous list of values (in a List<Object>), and I want to encode each of these using the appropriate encoder. However, the encode method of each encoder can only be called with the specific type that it accepts - it can't be called with a plain Object:
How can I call the encode method of the encoder?
I want to keep the encoders type-safe (i.e. keep the generics in the Encoder interface).
It can be done with reflection, but is there also another way without using reflection?
I'm not 100% sure this would work, but it's the first thing that springs to mind (and it compiles!): try wrapping the Encoder in a generic "untyped" encoder. Within that wrapper you have access to the generic type, so you can cast the object. Like this:Then you can use it like this:
You're going to need an unsafe cast there somewhere. You can try forcing encoder into an Encoder<Object> reference. The compiler sees it as unsafe, but as long as you guarantee that encoder and value match types then that shouldn't be a problem.
Matthew: nice try, but the extra class is unnecessary. With it I get two warning because you're creating a raw EncoderWrapper instance, then assigning that to the typed wrapper variable. I can get only one warning (unchecked cast) with only one line of code:
Rob Spoor wrote:Matthew: nice try, but the extra class is unnecessary. With it I get two warning because you're creating a raw EncoderWrapper instance, then assigning that to the typed wrapper variable.
Yeah, fair enough. My "theory" (which I felt was not quite right, but I didn't get any warnings so thought I might have got away with it) was that the typed encoder I was passing into the constructor would provide the generic type of the wrapper. But of course that doesn't actually work because the compiler doesn't know what type it is....
Getting the Encoder isn't the issue. It's calling the encode method of an Encoder<?> (type unknown; your example shows just why) using Object as argument. Sure, you could put the encoding within the if-statement, but I'm thinking Jesper's Encoder<?> lookup is a bit different than this one (perhaps using a Map?).
Remember that Java generics are a compile-time construct: they exist only in the mind of the compiler. Any code that has to make a runtime decision about unknown types is going to be forced to include unsafe casts, by definition. Fact of life, unfortunately.
Ofcourse there has to be an unchecked cast somewhere to do this, so it's not going to be 100% type-safe, but at least I can implement the encoders themselves in a type-safe way - one thing I thought of was leaving the generics out of the interface, and just pass or return Object, but that's not so good, especially since I need to implement a list of different encoders for different Java types.
It's actually the same situation as a question that comes up on the forums here every now and then - why you can't add elements to a List<?>: because the compiler doesn't know what the type "?" is, so it has no way to check that you pass the right type of object.