generics puzzle

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

Steven Simpson

One fix is to just introduce a method setFromModified() in GThing,
but that doesn't appeal to me.

Instead of adding it to GThing, create a static method:

private static<T> void setModified(GThing<T> t) {
t.set(t.modified());
}

This bit then compiles:

for (GThing<?> t : things)
setModified(t);

I think you can see it as temporarily pinning down T to span the two
calls, but you can only do that for a complete method, not in-line over
an expression or a few statements.
 
T

Tom Anderson

Instead of adding it to GThing, create a static method:

private static<T> void setModified(GThing<T> t) {
t.set(t.modified());
}

This bit then compiles:

for (GThing<?> t : things)
setModified(t);

I think you can see it as temporarily pinning down T to span the two
calls, but you can only do that for a complete method, not in-line over
an expression or a few statements.

+1

We really need a name for this manoeuvre. Can we call it "taming the
wildcard" or something?

tom
 
M

markspace

Instead of adding it to GThing, create a static method:

private static<T> void setModified(GThing<T> t) {
t.set(t.modified());
}


I don't like the T here. While it compiles, this T is a different T
than the other T.

public class GThing<T> {

private static <U> void setModified( GThing<U> t ) {

...other T's stay the same.
}
 
S

Steven Simpson

I don't like the T here. While it compiles, this T is a different T
than the other T.

Ah, T time! ;-)
public class GThing<T> {

private static <U> void setModified( GThing<U> t ) {

...other T's stay the same.
}

I meant that the static method didn't belong in GThing at all. The
author of the call site factors it out of his code, and keeps it near
the call site, in order to deal with an issue that arises very
specifically out of the design of that site. Ideally, it would be an
Ada-like local method, but he has to settle for a near-by static. The
author of GThing is unaware of it, and is not responsible for it.
 
A

Andreas Leitgeb

markspace said:
I don't like the T here. While it compiles, this T is a different T
than the other T.

If you want to mix GThing<Apple>s and GThing<Orange>s, then you had
better go by GThing<Fruit> all the way.
 
D

Daniel Pitts

Instead of adding it to GThing, create a static method:

private static<T> void setModified(GThing<T> t) {
t.set(t.modified());
}

This bit then compiles:

for (GThing<?> t : things)
setModified(t);

I think you can see it as temporarily pinning down T to span the two
calls, but you can only do that for a complete method, not in-line over
an expression or a few statements.



What about the simpler solution:

in the GThing class:

public void setModified() {
set(modified());
}
 
R

Robert Klemme

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.  :)? )

The reason is the ? in the type: the compiler does not know the type.
The very moment you declare a List<GThing<?>> the type of T is unknown
even though it's the same instance. Unknown types cannot be
compatible yet you try to do invoke a method with argument type ? with
a parameter of type ? and since "? incompatible to ?" it is not
allowed.

You are basically doing this (list1) but bounding does not help
(neither way, list2 and list3); only concrete types are able to ensure
type compatibility:

import java.util.ArrayList;
import java.util.List;

public class GenericsProblemRK {

public static void main(String[] args) {
final List<List<?>> list1 = new ArrayList<List<?>>();

for (final List<?> l : list1) {
l.add(l.get(0)); // error
final Object x = l.get(0); // OK
l.add(x); // error
}

final List<List<? extends String>> list2 = new ArrayList<List<?
extends String>>();

for (final List<? extends String> l : list2) {
l.add(l.get(0)); // error
final Object x = l.get(0); // OK
l.add(x); // error
}

final List<List<? super String>> list3 = new ArrayList<List<? super
String>>();

for (final List<? super String> l : list3) {
l.add(l.get(0)); // error
final Object x = l.get(0); // OK
l.add(x); // error
}

final List<List<String>> list4 = new ArrayList<List<String>>();

for (final List<String> l : list4) {
l.add(l.get(0)); // OK
}
}

}
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 your case since apparently you want to update internally why not
just introduce

public void update() {
set(modified());
}

This will safely compile. Basically this is what you did with
setModified() in the wrapper class. All other approaches will be
hacky (i.e. involve explicit casting, using Object parameters or
return values etc.).

Kind regards

robert
 
S

Steven Simpson

What about the simpler solution:

in the GThing class:

public void setModified() {
set(modified());
}

It "doesn't appeal" to the OP? Nor to me. Not sure I can put my finger
on it, but if I were the maintainer of GThing, I'd consider its
specification to be complete without this method. It doesn't increase
the value of the class as an abstraction. Adding it would certainly
just be a convenience for the caller. It solves a problem generated by
the design of the call site, not the design of the class's contract.
The caller can write his own routine as necessary.
 
B

blmblm

Hm, yes, that works; thanks!
Ah, T time! ;-)


I meant that the static method didn't belong in GThing at all.

That was kind of my feeling, but -- I'm kind of ambivalent about
it, because there are a couple of "call sites" (to use your term)
where I seem to need this kind of thing.
The
author of the call site factors it out of his code, and keeps it near
the call site, in order to deal with an issue that arises very
specifically out of the design of that site. Ideally, it would be an
Ada-like local method, but he has to settle for a near-by static. The
author of GThing is unaware of it, and is not responsible for it.

Wrong gender for the author of the call site here, but okay, pet peeve
of mine, and off-topic, and IIRC discussed recently enough that we
shouldn't repeat it ....
 
B

blmblm

The reason is the ? in the type: the compiler does not know the type.
The very moment you declare a List<GThing<?>> the type of T is unknown
even though it's the same instance. Unknown types cannot be
compatible yet you try to do invoke a method with argument type ? with
a parameter of type ? and since "? incompatible to ?" it is not
allowed.

Nice job of putting into words my vague idea about what's wrong;
thanks!
You are basically doing this (list1) but bounding does not help
(neither way, list2 and list3); only concrete types are able to ensure
type compatibility:

import java.util.ArrayList;
import java.util.List;

public class GenericsProblemRK {

public static void main(String[] args) {
final List<List<?>> list1 = new ArrayList<List<?>>();

for (final List<?> l : list1) {
l.add(l.get(0)); // error
final Object x = l.get(0); // OK
l.add(x); // error
}

final List<List<? extends String>> list2 = new ArrayList<List<?
extends String>>();

for (final List<? extends String> l : list2) {
l.add(l.get(0)); // error
final Object x = l.get(0); // OK
l.add(x); // error
}

final List<List<? super String>> list3 = new ArrayList<List<? super
String>>();

for (final List<? super String> l : list3) {
l.add(l.get(0)); // error
final Object x = l.get(0); // OK
l.add(x); // error
}

final List<List<String>> list4 = new ArrayList<List<String>>();

for (final List<String> l : list4) {
l.add(l.get(0)); // OK
}
}

}
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 your case since apparently you want to update internally why not
just introduce

public void update() {
set(modified());
}

I'm not sure I can say why this doesn't appeal to me -- something
about not wanting to clutter up GThing with unnecessary methods.
This will safely compile. Basically this is what you did with
setModified() in the wrapper class. All other approaches will be
hacky (i.e. involve explicit casting, using Object parameters or
return values etc.).

Yeah, maybe .... In my "real" code (quotation marks because it's
a toy project embarked on for entertainment and a bit of education)
the method in the wrapper class does more than just call modified()
and then set(). I don't know that that matters much, though. I do
have a couple of different "call sites" (to use another poster's term)
that need similar functionality, so maybe it *does* go in GThing.
Well, like I said, "entertainment and a bit of education", and for
me that seems to involve -- the coding equivalent of periodically
rearranging the furniture, maybe.
 
R

Robert Klemme

Nice job of putting into words my vague idea about what's wrong;
thanks!

Thank _you_.
I'm not sure I can say why this doesn't appeal to me -- something
about not wanting to clutter up GThing with unnecessary methods.

Why is it unnecessary? It seems this provides a proper abstraction. As
your example shows and concluding from what you write below you do
already have two or more uses for it. So by having the method you can
actually reduce redundancy. That's a common pattern: we find we do
something over and over again, we turn it into a method or procedure.
Yeah, maybe .... In my "real" code (quotation marks because it's
a toy project embarked on for entertainment and a bit of education)
the method in the wrapper class does more than just call modified()
and then set(). I don't know that that matters much, though. I do
have a couple of different "call sites" (to use another poster's term)
that need similar functionality, so maybe it *does* go in GThing.
Well, like I said, "entertainment and a bit of education", and for
me that seems to involve -- the coding equivalent of periodically
rearranging the furniture, maybe.

That's called "refactoring" - and is quite frequent with "real" code in
my experience. :) Certainly not something to be afraid of - with
proper tools it can even be fun. :)

I think you're on the right track: best learning experience is by having
a toy project and trying out different things. That way you see what
works and what not - and you get the satisfaction of having found out
yourself.

Kind regards

robert
 
D

Daniel Pitts

It "doesn't appeal" to the OP? Nor to me. Not sure I can put my finger
on it, but if I were the maintainer of GThing, I'd consider its
specification to be complete without this method. It doesn't increase
the value of the class as an abstraction. Adding it would certainly just
be a convenience for the caller. It solves a problem generated by the
design of the call site, not the design of the class's contract. The
caller can write his own routine as necessary.
Possibly, but if it is something that happens frequently, then it seems
more likely to belong to GThing than externally. Otherwise your
call-site is suffering from the code smell "feature envy"

One other approach is a "visitor" pattern:


public interface GThingVisitor {
<T> void visit(GThing<T> gThing);
};


public class SetModifiedGThingVisitor {
<T> void visit(GThing<T> gThing) {
gThing.set(gThing.modified());
}
}

public class GThing<T> {
public void accept(GThingVisitor visitor) {
visitor.accept(this);
}
}

public class CallSite {
public void setAllModified(Iterable<? extends GThing<?>> things) {
GThingVisitor visitor = new SetModifiedGThingVisitor();
for (GThing<?> thing: things) {
thing.accept(visitor);
}
}
}
 
B

blmblm

Thank _you_.


Why is it unnecessary? It seems this provides a proper abstraction. As
your example shows and concluding from what you write below you do
already have two or more uses for it. So by having the method you can
actually reduce redundancy. That's a common pattern: we find we do
something over and over again, we turn it into a method or procedure.

Yeah, maybe. I'll look some more at my actual code.
That's called "refactoring" - and is quite frequent with "real" code in
my experience. :) Certainly not something to be afraid of - with
proper tools it can even be fun. :)

Well, I obviously think it's fun, or I wouldn't do so much of
it? I do know the term "refactoring", but to me it suggests an
activity somehow more purposeful than what I feel like I'm doing.
Good to know, by the way, that it does happen with real code --
I've been away from professional programming for a long time and
don't keep up as well as I might with current practices.

(About tools -- I'm a long-time vim user, more than a little
fanatical about my text editor of choice, but more and more for
Java code I find myself also starting Eclipse to do some of the
things *it* does well, and finding more and more things in that
category -- automatic generation of imports and boilerplate code,
renaming of classes, etc.)
I think you're on the right track: best learning experience is by having
a toy project and trying out different things. That way you see what
works and what not - and you get the satisfaction of having found out
yourself.

True. (For what it's worth -- I may be less of a novice than I'm
coming across as, having written my first program in 1970-something.
But the learning doesn't stop, or shouldn't?)
 
R

Robert Klemme

Well, I obviously think it's fun, or I wouldn't do so much of
it? I do know the term "refactoring", but to me it suggests an
activity somehow more purposeful than what I feel like I'm doing.

I think any rearrangement of code can be called "refactoring".
Sometimes you need to try out different arrangements until you arrive at
one which leads to satisfactory results.
Good to know, by the way, that it does happen with real code --
I've been away from professional programming for a long time and
don't keep up as well as I might with current practices.

(About tools -- I'm a long-time vim user, more than a little
fanatical about my text editor of choice, but more and more for
Java code I find myself also starting Eclipse to do some of the
things *it* does well, and finding more and more things in that
category -- automatic generation of imports and boilerplate code,
renaming of classes, etc.)

For me it's the other way round: I normally use Eclipse and use vim from
time to time - mostly for editing scripts or other text files that I
need to work with. :)
True. (For what it's worth -- I may be less of a novice than I'm
coming across as, having written my first program in 1970-something.
But the learning doesn't stop, or shouldn't?)

Then you certainly started earlier than me. :)

Kind regards

robert
 
E

Eight of Seventeen

(About tools -- I'm a long-time vim user, more than a little
fanatical about my text editor of choice, but more and more for
Java code I find myself also starting Eclipse to do some of the
things *it* does well, and finding more and more things in that
category -- automatic generation of imports and boilerplate code,
renaming of classes, etc.)

Ah. Progress at last.

Though I did notice some kooky recommendations about "screen" in
another thread. Did you really say you liked it for its providing a
somewhat-broken, but maybe somewhat-usable, implementation of cross-
app cut and paste? When, of course, graphical apps have had working
universal clipboards for, well, forever, that aren't limited to one
screenful at a time and aren't likely to capture crud like
foo^]]E^]]D^Hquux ...
 
M

markspace

(About tools -- I'm a long-time vim user, more than a little
fanatical about my text editor of choice, but more and more for
Java code I find myself also starting Eclipse to do some of the
things *it* does well, and finding more and more things in that
category -- automatic generation of imports and boilerplate code,
renaming of classes, etc.)


Yes, I used to be a fan of vim also, but modern IDEs, at least in the
Java space, do too much to ignore them. Macros/templates, automatic
generation of source code, automatic test environments, automatic
interfacing with SCC systems, etc. Vim just can't keep up. Emacs might
be able to keep up, but I was never an emacs fan, and I don't intend to
learn an older IDE.

Try to learn a couple of IDEs, it'll help your Java career immensely.
 
B

blmblm

Possibly, but if it is something that happens frequently, then it seems
more likely to belong to GThing than externally. Otherwise your
call-site is suffering from the code smell "feature envy"

One other approach is a "visitor" pattern:

I know the "visitor" pattern (vaguely anyway), but .... I'm not sure
I understand your version of it here, which -- does this compile?
because ....
public interface GThingVisitor {
<T> void visit(GThing<T> gThing);
};

(Aside: Hm, a generic method in an interface?! well, why not,
maybe, but I'm not sure it would have occurred to me to try!)
public class SetModifiedGThingVisitor {
<T> void visit(GThing<T> gThing) {
gThing.set(gThing.modified());
}
}

Was this meant to implement GThingVisitor?
 
B

blmblm

[ snip ]
Yeah, maybe. I'll look some more at my actual code.

What may matter, though, is that sometimes I want to immediately
pass the value returned by modified() to set(), while other times
I want to save the value returned by modified() locally, from where
it might or might not later be passed to set().

My most recent refactoring of the "real" code uses static generic
methods (as suggested somewhere upthread), and for now that seems
not-bad. I'll probably change my mind again at some point. :)?

[ snip ]
 
B

blmblm

Ah. Progress at last.

"At last"?
Though I did notice some kooky recommendations about "screen" in
another thread. Did you really say you liked it for its providing a
somewhat-broken, but maybe somewhat-usable, implementation of cross-
app cut and paste?

One of the things I like about it is that it provides a mechanism
for cut and paste, yes. Why do you call it "somewhat-broken"?

(Be advised that I'm talking about the GNU version of "screen",
not the non-GNU version, which I haven't used in a while but
which I seem to remember as having fewer features and little if
any documentation.)
When, of course, graphical apps have had working
universal clipboards for, well, forever,

For suitable values of "forever" .... And I sometimes find it
somehow soothing to work in a non-graphical environment. YMMV.
that aren't limited to one
screenful at a time

The cut-and-paste provided by "screen" is not limited to one
screenful at a time.
and aren't likely to capture crud like
foo^]]E^]]D^Hquux ...

I have no idea what that "crud like ...." phrase is meant to convey;
it doesn't resemble anything I've observed happening with "screen".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top