Generics: ClassCastException: java.lang.Object[] cannot be cast to - java

I I have a simple class that uses generics.
public class ResponseWorkerRunnable<Parameter, Result> implements Runnable {
private final ResponseWorker<Parameter, Result> worker;
/**
* The parameters for {#link ResponseWorker#doInBackground(Object...)}
*/
private final Parameter[] params;
public ResponseWorkerRunnable(ResponseWorker<Parameter, Result> worker,
Parameter... params) {
uiThreadHandler = new Handler(Looper.getMainLooper());
this.worker = worker;
this.params = params;
}
#Override
public void run() {
try {
Result res = worker.doInBackground(params);
postResultBack(res);
} catch (Exception e) {
postErrorBack(e);
}
}
}
and my ResponseWorker:
public interface ResponseWorker<Parameter, Result> {
public Result doInBackground(Parameter... param) throws Exception;
}
The problem is, that I get ClassCastException:
java.lang.ClassCastException: java.lang.Object[] cannot be cast to
model.Table[]
I do something like this:
Table t = new Table();
ResponseWorker<Table, SuperTable> worker = ... ;
ResponseWorkerRunnable<Table, SuperTable> r = new ResponseWorkerRunnable<Table, SuperTable>(worker, t);
Than the ResponseWorkerRunnable will be scheduled and will run in the future with this exception:
java.lang.ClassCastException: java.lang.Object[] cannot be cast to
model.Table[]
at this line in the ResponseWorkerRunnable run() method:
Result res = worker.doInBackground(params);
I have used the debugger to check the Parameter[] params field (in ResponseWorkerRunnable) and its set to Object[1]{Table#4237c0e0}
So its an array of object but ResponseWorker.doInBackground expects an Array of class Table.
How do I cast it correctly?
Result res = worker.doInBackground((Parameter[]) params);
Any other idea or hint what could be wrong?
------ UPDATE -------
I use a singleton class called ResponseWorkerExecutor schedule the ResponseWorkerRunnable (with a ThreadPool) to
class ResponseWorkerExecutor {
public static <Parameter, Result> Future<?> submit(
ResponseWorker<Parameter, Result> responseWorker, Parameter ... params) {
return INSTANCE.executor
.submit(new ResponseWorkerRunnable<Parameter, Result>(
responseWorker, params));
}
}
So in my code I do something like this:
I do something like this:
Table t = new Table();
// ResponseWorker implementation
ResponseWorker<Table, SuperTable> worker = ... ;
// Here is the problem with the args
ResponseWorkerExecutor.submit(worker, t);

Due to the way generics work in Java (read here about type erasure) the actual Parameter class is being replaced by Object in the resulting bytecode, this is why your varargs array is Object[] and not Table[].
There is a workaround in this case that should work, it involves some changes to your code:
// Pass the actual class object to your Runnable, in this case t.getClass() -> Table.class
ResponseWorkerRunnable<Table, SuperTable> r = new ResponseWorkerRunnable<Table, SuperTable>(worker, t, t.getClass());
And then:
public class ResponseWorkerRunnable<Parameter, Result> implements Runnable {
private final ResponseWorker<Parameter, Result> worker;
/**
* The parameters for {#link ResponseWorker#doInBackground(Object...)}
*/
private final Parameter[] params;
private final Class<?> actualClass;
public ResponseWorkerRunnable(ResponseWorker<Parameter, Result> worker, Parameter... params, Class<?> actualClass) {
uiThreadHandler = new Handler(Looper.getMainLooper());
this.worker = worker;
this.params = params;
this.actualClass = actualClass;
}
#Override
public void run() {
try {
Result res = worker.doInBackground((Parameter[]) Arrays.copyOf(params, params.length, actualClass));
postResultBack(res);
} catch (Exception e) {
postErrorBack(e);
}
}
}
What this does is take the Object[] and copying its contents into a new, real Parameter[], whatever the actual class Parameter refers to. Then it makes the varargs call using this new array.

Using "Parameters" instead of the conventional "P" makes your code harder to read. What is happening is this. The type of this.params is correctly set to Parameter[]. If you passed a value to the constructor, then it would also be checked against Parameter[]. However, you didn't pass an argument, so the runtime creates an empty array for you. Unfortunately, it isn't smart enough to recognize the now erased type Parameter, so it creates an Object[]. I don't know if it should or not, but it isn't.
I understand what you are doing, and it makes sense. One way to "fix" the problem inside the constructor is to check the type of "params". Given that it is an array, you may not be able to use instanceof. Or, you can simply check to see if it empty. If you didn't receive a Parameter[], ignore "params" and create a new, emtpy Parameter[] and assign it to "this.params".

Use, that fixes it (at least thats what my Eclipse said ;-) )
public Result doInBackground(Parameter[] param);
If that fixed it, there seems to be a problem with the varags declaration and generics.

So I have sold this problem with a workaround. I use List instead of Parameter[] or Parameter ... params .
There are already some help methods in java.util.Collections class like: Collections.singletonList(param);
So in my case that seems to me the best solution, since I have only a single line of code, where I have to put a single object in a List<> or to convert a array to a list. Hence this method is part of a little library the user of the library does not have to take care about it.
The solution with Arrays.copyOf(params, params.length, actualClass)); suggested by #gpeche needs an aditional Class as parameter, and at the end the user of the library have to add the class.
So I guess I found a compromise by using List instead of Parameter ... params

Related

How to cast generic type to Object?

The code below won't compile because it can't add Task<T> to List<Task<Object>> how to fix it?
import java.util.ArrayList;
import java.util.List;
public class Tasks {
private final List<Task<Object>> queue = new ArrayList<>();
public static abstract class Task<T> {}
public <T> List<T> process(List<Task<T>> tasks) {
for (Task<T> task : tasks) queue.add(task); // <== problem here
}
}
Imagine Task was exactly like List: It has an add(T item) method.
If you allowed adding a Task<T> to a List of Task<Object>, then other code could then grab an item from the list, which would have type Task<Object>, which would let you thus call the add(Object o) method, letting you add anything you want, as anything is an object.
So if you added a Task<String>, your task now has non-strings in it.
That's why this isn't allowed.
Now, presumably, your task has absolutely no opportunities to 'add' things; none of the methods have a T as parameter, the T occurs solely in a 'get' position, in academicese, as a covariant option: For example, it only shows up in return types of methods in the Task class.
Java unfortunately does not have the kind of use-site variance declaration model that makes this easy.
You have two general solutions:
'uglycasting': Cast things, using the 3rd form of the cast construct, where you cast to a type that has generics, or perhaps, a raw type:
Task /* raw */ temp = task;
queue.add(temp); // this will work, but generates a warning.
then use #SuppressWarnings. Note that this does mean you're 'opting out' of the compiler checking your work, and if Task either has a method like add(T) or gets one later, you get no protections, and the end result will likely be that you end up seeing ClassCastException errors in places with zero casts in them.
Redefine queue.
Alternatively, redefinine queue to accept covariance. In that sense, Task<Object> is rather useless; few to no task types you'll ever make are actually assignable to this type. Try Task<? extends Object>, which can be shortened to just Task<?>. Just like with a List<? extends Whatever>, you cannot invoke add() style methods at all on such a thing; any method Task has that takes as parameter a T are now not invokable*. That's a good thing; it frees you up to assign any Task<T> regardless of what T might be to it, then.
List<Task<?>> queue = new ArrayList<>();
*) Unless you pass literally null, which is every type, and thus also fits for an unknown bound, but that's rather useless, of course.
If you are storing mixed task types, you need to use ?, since your list does not know what kind of Task is going to be passed into it.
private final List<Task<?>> queue = new ArrayList<>();
import java.util.*;
import java.util.stream.Collectors;
public class Tasks {
// Static classes, methods and fields
public static abstract class Task<T> {
private T value;
public T getValue() { return value; }
protected Task(T value) { this.value = value; }
public String toString() { return String.format("Task[value=%s]", this.value); }
}
// Instance fields
private final List<Task<?>> queue = new ArrayList<>();
// Class methods
public List<?> process(List<Task<?>> tasks) {
for (Task<?> task : tasks) queue.add(task);
return queue.stream().map(Task::getValue).collect(Collectors.toList());
}
// Implementation
private static class StringTask extends Task<String> {
protected StringTask(String value) {
super(value);
}
}
private static class IntTask extends Task<Integer> {
protected IntTask(Integer value) {
super(value);
}
}
public static void main(String[] args) {
Tasks tasks = new Tasks();
List<Task<?>> taskList = Arrays.asList(
new StringTask("Hello World"),
new IntTask(42)
);
tasks.process(taskList).stream().forEach(System.out::println);
}
}

Is Reflection needed to apply the correct generic adapter to my object dynamically

I am currently working on a serialization routine which uses a library of generically typed adapters. If the object being serialized is an instance of one of the specific adapters I have, then I need to call that adapter on the object prior to performing my other serialization procedures.
The following code works:
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
Class<?>[] argTypes = new Class[] {clazz};
try {
Method method = adapter.getClass().getMethod("marshal", argTypes);
adaptedObj = method.invoke(adapter, obj);
break;
} catch (Exception e) {
// handle method retrieval and invocation related exceptions
}
}
}
// serialize
}
However, I had originally thought that I would be able to do this more simply, for example with code like:
/* DOES NOT WORK */
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(clazz.cast(obj));
break;
}
}
// serialize
}
Clearly the problem is that the wildcard generically typed adapter isn't guaranteed to handle an object of type clazz. However, I can't indicate that these two are the same by changing the method signature—as I might otherwise do—to private final static <T> String serialize(Object obj, Map<Class<T>, XmlAdapter<?,T>> classToAdapterMap), because the map needs to hold adapters of all different types.
What would be a better way to do this? Or should I stick with the Reflection based solution?
Thanks in advance,
-Dan
There are several solutions to circumvent this problem.
Most likely, the easiest one is using raw types: don't specify the type parameters for the adapter, and the compiler will happily accept the marshall call (with a raw type warning of course):
XmlAdapter adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
(This is actually roughly the same solution as Bastian's, without the intermediate type)
If you don't like raw types, you may choose the unchecked cast to an Object-parameterized adapter. It's not really better, but it also works (by tricking the compiler…):
XmlAdapter<?, Object> adapter = (XmlAdapter<?, Object>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
My last solution is to use a type parameter at the method level. This time, what you do is semantically correct (as long as the map itself is correct), and the unchecked cast really means “I know what I am doing here”:
private final static <T> String serialize(T obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
try {
XmlAdapter<?, ? super T> adapter = (XmlAdapter<?, ? super T>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
break;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// serialize
}
The semantic correctness comes from the following:
you may consider T to be the actual class of obj since T is a method-bound parameter, not used elsewhere in the signature;
clazz is a super type of the type of T since we checked clazz.isInstance(obj);
adapter can handle instances of clazz or a super-type of it since it is how the map was built;
by consequent, adapter can handle all instances of an (unknown) super type of T, hence the ? super T declaration.
There is a simpler and safer way without using reflection:
At first, we need a small specialization of the XmlAdapter as it allows us to ask the adapter for the type it can handle.
public abstract class TalkingXmlAdapter<ValueType, BoundType> extends XmlAdapter<ValueType, BoundType> {
public abstract Class<BoundType> getBoundType();
}
My custom adapters now need to extend TalkingXmlAdapter:
public class AppleXmlAdapter extends TalkingXmlAdapter<String, Apple> {
#Override
public Class<Apple> getBoundType() {
return Apple.class;
}
#Override
public Apple unmarshal(String v) throws Exception {
System.out.println("Unmarshalling Apple");
return new Apple();
}
#Override
public String marshal(Apple v) throws Exception {
System.out.println("Marshalling Apple");
return "Apple";
}
}
public class BananaXmlAdapter extends TalkingXmlAdapter<String, Banana> {
#Override
public Class<Banana> getBoundType() {
return Banana.class;
}
#Override
public Banana unmarshal(String v) throws Exception {
System.out.println("Unmarshalling Banana");
return new Banana();
}
#Override
public String marshal(Banana v) throws Exception {
System.out.println("Marshalling Banana");
return "Banana";
}
}
That allows us to write a simplified serialization method:
public class SimpleSerializer {
public static final String serialize(Object obj, List<TalkingXmlAdapter> allAdapters) throws Exception {
Object adaptedObj = null;
for (TalkingXmlAdapter adapter : allAdapters) {
if (adapter.getBoundType().isInstance(obj)) {
adaptedObj = adapter.marshal(obj);
break;
}
}
// serialize
System.out.println("Simple serializing for " + obj.toString());
return "Simply serialized " + obj.toString();
}
}
Using the code e.g. like in the subsequent listing shows the behavior you want:
List<TalkingXmlAdapter> allAdapters = new ArrayList<>();
allAdapters.add(new AppleXmlAdapter());
allAdapters.add(new BananaXmlAdapter());
SimpleSerializer.serialize(new Banana(), allAdapters);
SimpleSerializer.serialize("Lemmon", allAdapters);
SimpleSerializer.serialize(new Apple(), allAdapters);
Output:
Marshalling Banana
Simple serializing for generic.adapter.Banana#659e0bfd
Simple serializing for Lemmon
Marshalling Apple
Simple serializing for generic.adapter.Apple#2a139a55
To sum this up, the solution gives you following advantages:
You don't need reflection which simplifies your code.
You need fewer generic programming in your serialization routine which simplifies your code.
The solution is more safe. Note that no type cast is needed. Every adapter accepts the type Object. However by using the generic method getBoundType() you can ensure the specific runtime type is the correct one. When building your map as in the reflection solution, a wrongly mapped class results in a runtime exception. In the proposed solution the super class TalkingXmlAdapter enforces each adapter to state their correct type by using generics.
The price you pay is:
Introduction of a new super type.
Requires small adaptions for your custom adapters.
Hope that helps!

Obtaining types through parameters on Java

Certainly I'm quite new in all this Java stuff, so I have a question, I'm trying to deserialize a response obtained on a WCF service, everything works fine, but, I'm trying to make a generic function to do this.
Basically what I do is
public List<msg> GetService(String method){
List<msg> response = new ArrayList<msg>();
Type msgType = new TypeToken<List<msg>>(){}.getType();
//Obtaining result
response = uJSON.fromJson(serviceResponse, msgType);
//uJSON is an instance of Gson library, for deserializing it just needs
//the service response and a Class<T> or Type to reflect the obtained message
}
What I'm trying to do is obtaining the Type "msg" generic, it means...
public <thing> void GetInstanceService(String method){
List<thing> response = new ArrayList<thing>();
Type rType2 = new TypeToken<List<thing>>(){}.getType(); //Got java.util.List<thing>
//And when I'm trying to deserialize I just obtain a List of object
//[java.lang.Object#5c7a987e, java.lang.Object#74b1a7a0]
type2 = uJSON.fromJson(new String(entity), rType2);
}
But I'm calling like this.
comm.<msgType>GetInstanceService("listTestType");
So, when I call "GetInstanceService", "thing" is "msgType" Type, for the
List<thing> and also response shouldn't be List<msgType> instead of List <Object>?
Besides, when I'm trying to explicitly pass the type through a "Type" parameter, it just causes me compilation time error like this.
public void GetInstanceService(Type type){
List<type> type2 = new ArrayList<type>(); //Compilation time error
//Or
msgType oType = new msgType();
Class classType = oType.getClass();
List<classType> type3; //Compilation time error
}
So, if none of these attempts was effective, how could I set the type for deserialization?
Guava class TypeToken does not support that mode of usage. You are creating the type token with a type variable and there not enough information for it to reconstruct List<String> from List<T>. You should create an instance of TypeToken where you have all the required compile-time information.
The documentation says:
Note that it's critical that the actual type argument is carried by a
subclass. The following code is wrong because it only captures the <T>
type variable of the listType() method signature; while <String> is
lost in erasure:
class Util {
static <T> TypeToken<List<T>> listType() {
return new TypeToken<List<T>>() {};
}
}
TypeToken<List<String>> stringListType = Util.<String>listType();
But as said above, you can instantiate the TypeToken at call-site, where all type info are available, and then pass it as a parameter. Something like this:
public <thing> void GetInstanceService(String method, TypeToken<List<thing>> token){
List<thing> response = new ArrayList<thing>();
Type rType2 = token.getType();
type2 = uJSON.fromJson(new String(entity), rType2);
}
comm.GetInstanceService("listTestType", new TypeToken<List<msgType>>() {});
Update
Paul Bellora notes that you can also accept a parameter TypeToken<thing> token, and construct a TypeToken<List<thing>> inside the method from that token:
public <thing> void GetInstanceService(String method, TypeToken<thing> token) {
List<thing> response = new ArrayList<thing>();
Type rType2 = new TypeToken<List<thing>>() {}
.where(new TypeParameter<thing>() {}, token); // where() binds "thing" to token
.getType();
type2 = uJSON.fromJson(new String(entity), rType2);
}
comm.GetInstanceService("listTestType", new TypeToken<msgType>() {});
Due to something called type erasure, the class object you need is not available at runtime.
However, there is a standard work-around: pass a type token into your method, like this:
public <T> List<T> getService(String method, Class<T> c) {
// the caller has passed in the class object
List<T> list = new ArrayList<T>();
// fill list
return list;
}

Anything wrong with instanceof checks here?

With the introduction of generics, I am reluctant to perform instanceof or casting as much as possible. But I don't see a way around it in this scenario:
for (CacheableObject<ICacheable> cacheableObject : cacheableObjects) {
ICacheable iCacheable = cacheableObject.getObject();
if (iCacheable instanceof MyObject) {
MyObject myObject = (MyObject) iCacheable;
myObjects.put(myObject.getKey(), myObject);
} else if (iCacheable instanceof OtherObject) {
OtherObject otherObject = (OtherObject) iCacheable;
otherObjects.put(otherObject.getKey(), otherObject);
}
}
In the above code, I know that my ICacheables should only ever be instances of MyObject, or OtherObject, and depending on this I want to put them into 2 separate maps and then perform some processing further down.
I'd be interested if there is another way to do this without my instanceof check.
Thanks
You could use double invocation. No promises it's a better solution, but it's an alternative.
Code Example
import java.util.HashMap;
public class Example {
public static void main(String[] argv) {
Example ex = new Example();
ICacheable[] cacheableObjects = new ICacheable[]{new MyObject(), new OtherObject()};
for (ICacheable iCacheable : cacheableObjects) {
// depending on whether the object is a MyObject or an OtherObject,
// the .put(Example) method will double dispatch to either
// the put(MyObject) or put(OtherObject) method, below
iCacheable.put(ex);
}
System.out.println("myObjects: "+ex.myObjects.size());
System.out.println("otherObjects: "+ex.otherObjects.size());
}
private HashMap<String, MyObject> myObjects = new HashMap<String, MyObject>();
private HashMap<String, OtherObject> otherObjects = new HashMap<String, OtherObject>();
public Example() {
}
public void put(MyObject myObject) {
myObjects.put(myObject.getKey(), myObject);
}
public void put(OtherObject otherObject) {
otherObjects.put(otherObject.getKey(), otherObject);
}
}
interface ICacheable {
public String getKey();
public void put(Example ex);
}
class MyObject implements ICacheable {
public String getKey() {
return "MyObject"+this.hashCode();
}
public void put(Example ex) {
ex.put(this);
}
}
class OtherObject implements ICacheable {
public String getKey() {
return "OtherObject"+this.hashCode();
}
public void put(Example ex) {
ex.put(this);
}
}
The idea here is that - instead of casting or using instanceof - you call the iCacheable object's .put(...) method which passes itself back to the Example object's overloaded methods. Which method is called depends on the type of that object.
See also the Visitor pattern. My code example smells because the ICacheable.put(...) method is incohesive - but using the interfaces defined in the Visitor pattern can clean up that smell.
Why can't I just call this.put(iCacheable) from the Example class?
In Java, overriding is always bound at runtime, but overloading is a little more complicated: dynamic dispatching means that the implementation of a method will be chosen at runtime, but the method's signature is nonetheless determined at compile time. (Check out the Java Language Specification, Chapter 8.4.9 for more info, and also check out the puzzler "Making a Hash of It" on page 137 of the book Java Puzzlers.)
Is there no way to combine the cached objects in each map into one map? Their keys could keep them separated so you could store them in one map. If you can't do that then you could have a
Map<Class,Map<Key,ICacheable>>
then do this:
Map<Class,Map<Key,ICacheable>> cache = ...;
public void cache( ICacheable cacheable ) {
if( cache.containsKey( cacheable.getClass() ) {
cache.put( cacheable.getClass(), new Map<Key,ICacheable>() );
}
cache.get(cacheable.getClass()).put( cacheable.getKey(), cacheable );
}
You can do the following:
Add a method to your ICachableInterface interface that will handle placing the object into one of two Maps, given as arguments to the method.
Implement this method in each of your two implementing classes, having each class decide which Map to put itself in.
Remove the instanceof checks in your for loop, and replace the put method with a call to the new method defined in step 1.
This is not a good design, however, because if you ever have another class that implements this interface, and a third map, then you'll need to pass another Map to your new method.

Java Generics: casting to ? (or a way to use an arbitrary Foo<?>)

So, I have some code that looks approximately like (truncated for brevity - ignore things like the public member variables):
public class GenericThingy<T> {
private T mValue;
public final T[] mCandidates;
public GenericThingy(T[] pCandidates, T pInitValue) {
mCandidates = pCandidates;
mValue = pInitValue;
}
public void setValue(T pNewValue) {
mValue = pNewValue;
}
}
public class GenericThingyWidget {
private final GenericThingy<?> mThingy;
private final JComboBox mBox;
public GenericThingyWidget (GenericThingy<?> pThingy) {
mThingy = pThingy;
mBox = new JComboBox(pThingy.mCandidates);
//do stuff here that makes the box show up
}
//this gets called by an external event
public void applySelectedValue () {
mThingy.setValue(mBox.getSelectedItem());
}
}
}
My problem is that the mThingy.setValue(mBox.getSelectedItem()); call generates the following error:
The method setValue(capture#4-of ?) in the type Generics.GenericThingy<capture#4-of ?> is not applicable for the arguments (Object)
I can get around this by removing the <?> from the declaration of mThingy and pThingy in GenericThingyWidget - which gives me a "GenericThingy is a raw type. References to GenericThingy should be parameterized" warning.
I also tried replacing the setValue call with
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
which I genuinely expected to work, but that produced a very similar error:
The method setValue(capture#4-of ?) in the type Generics.GenericThingy<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)
Is there any way to do this without generating "raw type" warnings ("unchecked cast" warnings I'm OK with) and without making GenericThingyWidget into a generic type? I'd think I could cast the return of mBox.getSelectedItem() to something, but I can't figure out what that would be.
As a bonus question, why does the replacement call to mThingy.setValue not work?
You lack information in GenericThingyWidget.
The ? you put means : any class extending object. Which means any, not some particular one but I don't know which one. Java can't relate one ? to another, they can not be related one to the other in a class hierarchy tree. So
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
this tries to put an object of any class in the setValue, which is waiting for any other class, but the ? can not tell Java these two any should be the same class.
Without parameterizing GenericThingyWidget, I don't see any way to work around it.
What I would do : parameterize GenericThingyWidget, and create a Factory static parameterized method :
public static <T> GenericThingyWidget<T> make(T someObject){
...
}
I see two possibilities.
With a private addition to GenericThingyWidget— Goetz's capture helper pattern:
public void applySelectedValue() {
helper(mThingy, mBox.getSelectedIndex());
}
private static <T> void helper(GenericThingy<T> pThingy, int pIndex) {
pThingy.setValue(pThingy.mCandidates[pIndex]);
}
Or, quick-and-dirty, with a modification to the API of GenericThingy:
public void setValue(int value) {
mValue = mCandidates[value];
}
As a bonus question, why does the replacement call to mThingy.setValue not work?
The article by Brian Goetz probably explains this better than I will, but I'll give it a try.
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
The compiler knows that mThingy has some type parameter, but it doesn't know what the that type is, because it is a wildcard. It creates a placeholder for this type—"capture#4-of ?". The compiler also knows that mCandidates has some type, but it doesn't know what it is either. It creates brand new "capture" type—"capture#5-of ?" While you and I can reason that these should be the same type, the compiler (at least for now) can't jump to that conclusion. Thus, you get the error message.
The capture helper gets around that. Although the compiler doesn't know what the type is, it knows it has a type, so it allows you to pass it to the helper method. Once inside the helper method, there are no wildcards, and the compiler doesn't have to do any reasoning about whether the wildcards really refer to the same type.
Update
OK, try this:
public class GenericThingy<T> {
private Class<T> mClazz;
private T mValue;
public final T[] mCandidates;
public GenericThingy(Class<T> clazz, T[] pCandidates, T pInitValue) {
mClazz = clazz;
mCandidates = pCandidates;
mValue = pInitValue;
}
public void setValue(Object newValue) throws ClassCastException {
mValue = mClazz.cast(newValue);
}
}
What you need to to is parameterize GenericThingyWidget like so:
public class GenericThingyWidget<T> {
private final GenericThingy<? super T> mThingy;
private final JComboBox mBox;
public GenericThingyWidget (GenericThingy<? super T> pThingy) {
mThingy = pThingy;
mBox = new JComboBox(pThingy.mCandidates);
//do stuff here that makes the box show up
}
//this gets called by an external event
public void applySelectedValue () {
mThingy.setValue((T) mBox.getSelectedItem());
}
}
}
Technically, you don't need the ? super T for your example, and would be fine with just a T, and perhaps it would be better in real code if you ever want to get from the GenericThingy instead of just inserting into it.
As KLE said, You can just de-parameterize GenericThingy (replace all the T's with objects). In fact, I think you have to unless you plan to pass the class of T to the constructor of GenericThingyWidget, and then dynamically cast from your mbox.getSelectedItem(), since as far as I can tell, getSelectedItem() only returns Object.

Categories

Resources