B
blmblm
I'm having a bit of trouble with generics, and while I've come
up with ways of getting done what I want to get done, I wonder
whether there's some nice solution that isn't occurring to me.
The basic situation is this:
I have an abstract generic class (call it GThing) with type
parameter T, which has a method (call it set()) that takes a
parameter of type T and another method (call it modified()) that
returns a result of type T. I also have some classes that extend
GThing and specify its type parameter, and code that is meant
to operate on lists of instances of these various subclasses.
What I would *like* to be able to do is construct a list of
GThing<?> objects and then do to each element of it something
like the following:
element.set(element.modified())
but the compiler objects to that, which I find reasonable enough,
though I'm not enough of a generics expert to really articulate
what the problem is. (I have a feeling that this "type erasure"
thing, which I understand dimly at best, comes into it. ? )
One fix is to just introduce a method setFromModified() in GThing,
but that doesn't appeal to me. An ugly-hack fix is to add to
GThing a set() method that takes an Object and typecasts it,
but -- yuck, right?
In the same code I also have another generic class that "wraps"
GThing (call it GThingWrapper), and a class that among other
things contains a list of objects of the wrapper class (call
it GThingWrapperList). One thing I want to do is add to that
list of objects a new GThingWrapper constructed from a GThing,
with the type parameter of the GThingWrapper matching the type
parameter of the GThing. Initially that didn't work either,
which also seems reasonable enough, *but* I discovered (by trial
and error) a way to make it work using a generic method. Now I'm
wondering whether somehow a generic method could be used to solve
the first problem, but I'm not finding a way to do it.
SSCCEs follow, one for the first situation (with a comment quoting
the error message I get from Oracle's "javac" compiler, version
1.6.0_21 if that matters), and one for the second.
I'd be glad of advice from anyone knowledgeable about generics
and willing to wade through my code ....
==== SSCCE #1 ====
import java.util.ArrayList;
import java.util.List;
public class GenericsProblem {
public static void main(String[] args) {
List<GThing<?>> things = new ArrayList<GThing<?>>();
things.add(new IntegerThing(1));
things.add(new IntegerThing(2));
things.add(new StringThing("hello"));
things.add(new StringThing("bye"));
for (GThing<?> t : things) {
System.out.println(t.get());
}
for (GThing<?> t : things) {
/*
t.set(t.modified());
does not compile:
set(capture#591 of ?) in
GenericsProblem.GThing<capture#591 of ?>
cannot be applied to (java.lang.Object)
*/
}
for (GThing<?> t : things) {
/* ugly hack!? */
t.setG(t.modified());
}
for (GThing<?> t : things) {
System.out.println(t.get());
}
}
public static abstract class GThing<T> {
private T val;
protected GThing(T v) { val = v; }
public T get() { return val; }
public void set(T v) { val = v; }
/* ugly hack!? */
@SuppressWarnings("unchecked")
public void setG(Object v) { val = (T) v; }
abstract T modified();
}
public static class IntegerThing extends GThing<Integer> {
public IntegerThing(Integer i) { super(i); }
@Override
public Integer modified() { return get() * 2; }
}
public static class StringThing extends GThing<String> {
public StringThing(String s) { super(s); }
@Override
public String modified() { return get() + get(); }
}
}
==== SSCCE #2 ====
import java.util.ArrayList;
import java.util.List;
public class GenericsProblemSoln {
public static void main(String[] args) {
GThingWrapperList thingWrappers = new GThingWrapperList();
thingWrappers.add(new IntegerThing(1));
thingWrappers.add(new IntegerThing(2));
thingWrappers.add(new StringThing("hello"));
thingWrappers.add(new StringThing("bye"));
for (GThingWrapper<?> tw : thingWrappers.elements()) {
System.out.println(tw.getThing().get());
}
for (GThingWrapper<?> tw : thingWrappers.elements()) {
tw.setModified();
}
for (GThingWrapper<?> tw : thingWrappers.elements()) {
System.out.println(tw.getThing().get());
}
}
public static abstract class GThing<T> {
private T val;
protected GThing(T v) { val = v; }
public T get() { return val; }
public void set(T v) { val = v; }
abstract T modified();
}
public static class IntegerThing extends GThing<Integer> {
public IntegerThing(Integer i) { super(i); }
@Override
public Integer modified() { return get() * 2; }
}
public static class StringThing extends GThing<String> {
public StringThing(String s) { super(s); }
@Override
public String modified() { return get() + get(); }
}
public static class GThingWrapper<T> {
private GThing<T> thing;
public GThingWrapper(GThing<T> t) { thing = t; }
public GThing getThing() { return thing; }
public void setModified() {
thing.set(thing.modified());
}
}
public static class GThingWrapperList {
private List<GThingWrapper<?>> thingWrappers =
new ArrayList<GThingWrapper<?>>();
public <T> void add(GThing<T> t) {
thingWrappers.add(new GThingWrapper<T>(t));
}
public Iterable<GThingWrapper<?>> elements() {
return thingWrappers;
}
}
}
up with ways of getting done what I want to get done, I wonder
whether there's some nice solution that isn't occurring to me.
The basic situation is this:
I have an abstract generic class (call it GThing) with type
parameter T, which has a method (call it set()) that takes a
parameter of type T and another method (call it modified()) that
returns a result of type T. I also have some classes that extend
GThing and specify its type parameter, and code that is meant
to operate on lists of instances of these various subclasses.
What I would *like* to be able to do is construct a list of
GThing<?> objects and then do to each element of it something
like the following:
element.set(element.modified())
but the compiler objects to that, which I find reasonable enough,
though I'm not enough of a generics expert to really articulate
what the problem is. (I have a feeling that this "type erasure"
thing, which I understand dimly at best, comes into it. ? )
One fix is to just introduce a method setFromModified() in GThing,
but that doesn't appeal to me. An ugly-hack fix is to add to
GThing a set() method that takes an Object and typecasts it,
but -- yuck, right?
In the same code I also have another generic class that "wraps"
GThing (call it GThingWrapper), and a class that among other
things contains a list of objects of the wrapper class (call
it GThingWrapperList). One thing I want to do is add to that
list of objects a new GThingWrapper constructed from a GThing,
with the type parameter of the GThingWrapper matching the type
parameter of the GThing. Initially that didn't work either,
which also seems reasonable enough, *but* I discovered (by trial
and error) a way to make it work using a generic method. Now I'm
wondering whether somehow a generic method could be used to solve
the first problem, but I'm not finding a way to do it.
SSCCEs follow, one for the first situation (with a comment quoting
the error message I get from Oracle's "javac" compiler, version
1.6.0_21 if that matters), and one for the second.
I'd be glad of advice from anyone knowledgeable about generics
and willing to wade through my code ....
==== SSCCE #1 ====
import java.util.ArrayList;
import java.util.List;
public class GenericsProblem {
public static void main(String[] args) {
List<GThing<?>> things = new ArrayList<GThing<?>>();
things.add(new IntegerThing(1));
things.add(new IntegerThing(2));
things.add(new StringThing("hello"));
things.add(new StringThing("bye"));
for (GThing<?> t : things) {
System.out.println(t.get());
}
for (GThing<?> t : things) {
/*
t.set(t.modified());
does not compile:
set(capture#591 of ?) in
GenericsProblem.GThing<capture#591 of ?>
cannot be applied to (java.lang.Object)
*/
}
for (GThing<?> t : things) {
/* ugly hack!? */
t.setG(t.modified());
}
for (GThing<?> t : things) {
System.out.println(t.get());
}
}
public static abstract class GThing<T> {
private T val;
protected GThing(T v) { val = v; }
public T get() { return val; }
public void set(T v) { val = v; }
/* ugly hack!? */
@SuppressWarnings("unchecked")
public void setG(Object v) { val = (T) v; }
abstract T modified();
}
public static class IntegerThing extends GThing<Integer> {
public IntegerThing(Integer i) { super(i); }
@Override
public Integer modified() { return get() * 2; }
}
public static class StringThing extends GThing<String> {
public StringThing(String s) { super(s); }
@Override
public String modified() { return get() + get(); }
}
}
==== SSCCE #2 ====
import java.util.ArrayList;
import java.util.List;
public class GenericsProblemSoln {
public static void main(String[] args) {
GThingWrapperList thingWrappers = new GThingWrapperList();
thingWrappers.add(new IntegerThing(1));
thingWrappers.add(new IntegerThing(2));
thingWrappers.add(new StringThing("hello"));
thingWrappers.add(new StringThing("bye"));
for (GThingWrapper<?> tw : thingWrappers.elements()) {
System.out.println(tw.getThing().get());
}
for (GThingWrapper<?> tw : thingWrappers.elements()) {
tw.setModified();
}
for (GThingWrapper<?> tw : thingWrappers.elements()) {
System.out.println(tw.getThing().get());
}
}
public static abstract class GThing<T> {
private T val;
protected GThing(T v) { val = v; }
public T get() { return val; }
public void set(T v) { val = v; }
abstract T modified();
}
public static class IntegerThing extends GThing<Integer> {
public IntegerThing(Integer i) { super(i); }
@Override
public Integer modified() { return get() * 2; }
}
public static class StringThing extends GThing<String> {
public StringThing(String s) { super(s); }
@Override
public String modified() { return get() + get(); }
}
public static class GThingWrapper<T> {
private GThing<T> thing;
public GThingWrapper(GThing<T> t) { thing = t; }
public GThing getThing() { return thing; }
public void setModified() {
thing.set(thing.modified());
}
}
public static class GThingWrapperList {
private List<GThingWrapper<?>> thingWrappers =
new ArrayList<GThingWrapper<?>>();
public <T> void add(GThing<T> t) {
thingWrappers.add(new GThingWrapper<T>(t));
}
public Iterable<GThingWrapper<?>> elements() {
return thingWrappers;
}
}
}