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

So, I have code that looks something like (truncated for brevity - ignores things like 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 mThingy.setValue (mBox.getSelectedItem ()); raises the following error:

Method setValue(capture#4-of ?)

in type is Generics.GenericThingy<capture#4-of ?>

not valid for arguments (Object)

I can work around this by removing <?>

mThingy and pThingy from the declaration in GenericThingyWidget - which gives me "GenericThingy is a raw type. GenericThingy parameter warning must be parameterized".

I also tried to replace the call to setValue

mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);

      

which I actually expected to work, but it threw a very similar error:

Method setValue(capture#4-of ?)

in type is Generics.GenericThingy<capture#4-of ?>

not valid for arguments(capture#5-of ?)

Is there a way to do this without generating "raw type" warnings ("unchecked throw" warnings, which I'm fine with) and without making GenericThingyWidget a generic type? I think I could revert the return of mBox.getSelectedItem () to something, but I can't figure out what it would be.

As a bonus question, why isn't the mThingy.setValue replacement call working?

+2


source to share


5 answers


I see two possibilities.

With a closed addon to GenericThingyWidget

- Goetz Bindings Sample:

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 an API modification GenericThingy

:

public void setValue(int value) {
  mValue = mCandidates[value];
}

      




As a bonus question, why isn't the mThingy.setValue replacement call working?

Brian Goetz's article probably explains it better than I do, but I'll give it a try.

mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);

      

The compiler knows that it mThingy

has some type parameter, but it does not know what the type is, because it is a pattern. It creates a placeholder for this type of capture # 4-out? The compiler also knows that it mCandidates

is of some type, but it does not know what it is. Does he create a new "capture" like "capture # 5"? While you and I can speculate that they must be of the same type, the compiler (at least not yet) cannot jump to that conclusion. Thus, you will receive an error message.

The capture assistant will bypass this. Although the compiler doesn't know what a type is, it knows it has a type, so it lets you pass it to a helper method. Sometime there are no wildcards inside a helper method, and the compiler doesn't need to do any reasoning about whether the wildcards are really the same type.

+2


source


You are missing information in GenericThingyWidget

. Put ?

means: any class extension object. This means any , not a specific one, but I don't know which one. Java cannot link one ?

to the other, they cannot link to each other in a class hierarchy tree. So,

mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);

      

this tries to put an object of any class in setValue, which expects any other class, but ?

can't tell Java these two any should be the same class.



Without parameterization, GenericThingyWidget

I see no way to get around this.

What I would do: parameterize GenericThingyWidget

and create a static parameterized Factory method:

public static <T> GenericThingyWidget<T> make(T someObject){
    ...
}

      

+3


source


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);
    }
  }

      

+1


source


What you need is to parameterize the GenericThingyWidget like this:

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 this? super T for your example, and it would be nice with just T, and it might be better in real code if you ever want to get from GenericThingy rather than just stick into it.

0


source


As KLE said, you can just dewax GenericThingy (replace all T objects with objects). In fact, I think you need, if you don't plan on passing the T class to the GenericThingyWidget constructor, then dynamically casting from your mbox.getSelectedItem (), since as far as I can tell, getSelectedItem () returns an Object.

0


source







All Articles