Confusion with templates, generics, and instanceof

G

Greg Boettcher

I am still in my first year of learning Java, so by all means type
this up as a "newbie" question if you like.

I have started working with templates and generics. I don't fully
understand the underlying mechanics of them. Either because of that or
for whatever other reason, I have been having some problems.

I've tried to distill my problems down to an illustrative test case:

<code>

public class FooWrapper<T> {
//-------------
// Instance variable:
private T foo;

//-------------
// Constructor:
public FooWrapper(T foo) {
this.foo = foo;
}
// Getter:
public T getFoo() {
return foo;
}
}

</code>

The above code seems perfectly fine. However, suppose I try writing an
equals method. Two possibilities come to mind. The first looks like
this:

<code>

public boolean equals(Object o) {
boolean result = false;
if (o instanceof T) {
T t = (T)(o);
if (foo.equals(t.getFoo())) {
result = true;
}
}
return result;
}

</code>

Basically this doesn't work because "o instanceof T" (where T is a
template) doesn't work.

The other possibility is:

<code>

public boolean equals(T t) {
boolean result = false;
if (foo.equals(t.getFoo())) {
result = true;
}
return result;
}

</code>


I don't know why this doesn't work, but Eclipse says the following:

(1) "Name clash: The method equals(T) of type FooWrapper<T> has the
same erasure as equals(Object) of type Object but does not override
it"
(2) "The method getFoo() is undefined for the type T"
 
A

Arne Vajhøj

Greg said:
I have started working with templates and generics. I don't fully
understand the underlying mechanics of them. Either because of that or
for whatever other reason, I have been having some problems.

I've tried to distill my problems down to an illustrative test case:

<code>

public class FooWrapper<T> {
//-------------
// Instance variable:
private T foo;

//-------------
// Constructor:
public FooWrapper(T foo) {
this.foo = foo;
}
// Getter:
public T getFoo() {
return foo;
}
}

</code>

The above code seems perfectly fine. However, suppose I try writing an
equals method. Two possibilities come to mind.
The other possibility is:

<code>

public boolean equals(T t) {
boolean result = false;
if (foo.equals(t.getFoo())) {
result = true;
}
return result;
}

</code>


I don't know why this doesn't work, but Eclipse says the following:

(1) "Name clash: The method equals(T) of type FooWrapper<T> has the
same erasure as equals(Object) of type Object but does not override
it"
(2) "The method getFoo() is undefined for the type T"

public boolean equals(FooWrapper t) {
boolean result = false;
if (foo.equals(t.getFoo())) {
result = true;
}
return result;
}

seems to compile. The difference should be obvious.

Arne
 
M

Mark Space

Arne said:
public boolean equals(FooWrapper t) {
boolean result = false;
if (foo.equals(t.getFoo())) {
result = true;
}
return result;
}

seems to compile. The difference should be obvious.


This does compile, but overloads equals() not overrides it. The
question is, which is desired? Overload, or override?

Going back to the first attempt:


@Override
public boolean equals( Object o )
{
boolean result = false;
//if (o instanceof T) {
if( o instanceof FooWrapper ) {
FooWrapper f = (FooWrapper) o;
@SuppressWarnings( "unchecked" )
T t = (T) f.getFoo();
if( foo.equals( t ) ) {
result = true;
}
}
return result;
}



Might be what you are after. (Note: compiled, but not tested.)

(Now I'm unclear why the cast (T) is required. Anyone know why
getFoo(), which returns type T, needs a cast?)
 
J

Joshua Cranmer

Mark said:
(Now I'm unclear why the cast (T) is required. Anyone know why
getFoo(), which returns type T, needs a cast?)

You calling getFoo on a raw type, so the return is the erased value,
i.e., Object.

In this case, it would probably be preferable to cast to Object instead,
since equals() takes in an Object parameter and the cast is meaningless
in any case.
 
M

Mark Space

Joshua said:
You calling getFoo on a raw type, so the return is the erased value,
i.e., Object.

In this case, it would probably be preferable to cast to Object instead,
since equals() takes in an Object parameter and the cast is meaningless
in any case.


Ah, yes. Obviously. I must be going blind.

@Override
public boolean equals( Object o )
{
boolean result = false;
if( o instanceof FooWrapper ) {
FooWrapper f = (FooWrapper) o ;
if( foo.equals( f.foo ) ) {
result = true;
}
}
return result;
}

Nice call on using "foo" as type Object. :)
 
A

Arne Vajhøj

Mark said:
This does compile, but overloads equals() not overrides it. The
question is, which is desired? Overload, or override?

I think the question is whether he want to test for equality with
a T or a FooWrapper.

Arne
 
M

Mark Space

Arne said:
I think the question is whether he want to test for equality with
a T or a FooWrapper.

Not a FooWrapper. Object. Or you overload when you almost certainly
mean to override.

T is interesting. But again, equals(T) overloads equals(Object). I'd
name such a method differently. "wrappedFooEquals(T)" or something.
 
A

Arne Vajhøj

Mark said:
Not a FooWrapper. Object. Or you overload when you almost certainly
mean to override.

T is interesting. But again, equals(T) overloads equals(Object). I'd
name such a method differently. "wrappedFooEquals(T)" or something.

Error #2 in the original code was because he was calling
getFoo on a T instead of a FooWrapper.

And the equals method should only return true for a FooWrapper.

Error #1 relates to overriding equals Object.

Arne
 
M

Mark Space

And before anyone else corrects me again:


@Override
public boolean equals( Object o )
{
boolean result = false;
if( o instanceof FooWrapper ) {
FooWrapper f = (FooWrapper) o ;
result = foo.equals( f.foo );
}
return result;
}


is a tad pithier. :)
 
L

Lew

Mark said:
And before anyone else corrects me again:


@Override
public boolean equals( Object o )
{
boolean result = false;
if( o instanceof FooWrapper ) {
FooWrapper f = (FooWrapper) o ;
result = foo.equals( f.foo );

You forgot to check for equality if (this.foo == null), risking NPE.
}
return result;
}


is a tad pithier. :)

You want pithy?

@Override
public boolean equals( Object o )
{
return (this == o) ||
(o instanceof FooWrapper
&&
(this.foo == null? (((FooWrapper) o).foo == null)
: this.foo.equals( ((FooWrapper) o).foo ))
);
}
 
G

Greg Boettcher

Error #2 in the original code was because he was calling
getFoo on a T instead of a FooWrapper.

And the equals method should only return true for a FooWrapper.

Error #1 relates to overriding equals Object.

I see now that these were indeed errors... Sadly, I didn't manage to
come up with a good clean test case.

I'll digest this stuff, and then maybe I'll reply tomorrow with
questions on anything I still don't understand. For now, a big thanks
to those who responded.

Greg
 
R

Roedy Green

I have started working with templates and generics. I don't fully
understand the underlying mechanics of them. Either because of that or
for whatever other reason, I have been having some problems.

for some background, see http://mindprod.com/jgloss/generics.html
--
Roedy Green Canadian Mind Products
http://mindprod.com

"Climate change is no longer a doomsday prophecy, it’s a reality."
~ Astrid Heiberg president of the International Federation of Red Cross and Red Crescent Societies
 
L

Lew

Greg said:
I have started working with templates and generics. ....
Basically this doesn't work because "o instanceof T" (where T is a
template) doesn't work.

Just a side note about nomenclature: these aren't "templates". Java doesn't
have "templates", it has "generics" and "type parameters".

Another subtlety is that generics don't exist at run-time; they're strictly a
compiler concept.

I suggest that you read the freely downloadable generics chapter from the
excellent /Effective Java/ by Joshua Bloch
<http://java.sun.com/docs/books/effective/>

Then, buy the book and study it.
 
G

Greg Boettcher

I have started working with templates and generics. I don't fully
understand the underlying mechanics of them. Either because of that or
for whatever other reason, I have been having some problems.

Okay, thanks for replying, everybody. With your help and a little
extra outside reading, I think I've learned what I needed to find out.

Lessons learned:

1. I had previously thought that the thing within the angle-brackets
of a generic (T in this case) was called a template. (I swear my
professor said this.) Now, consulting Wikipedia's page on generics in
Java, I guess T is called a type variable.

2. What I really wanted to know was whether you can test to see if an
object, o, belongs to the class specified by a type variable, T. The
condition (o instanceof T) doesn't seem to work. This was my main
reason for posting here, and it's too bad I picked a bad example for
it. Anyway, I am starting to think that this is impossible. If I'm
wrong, please let me know.

3. I should be more careful when posting example code to Usenet.

4. And, yes, I should probably read more of the official
documentation. I didn't this time because I'm under a time crunch.

Greg
 
L

Lew

Greg said:
2. What I really wanted to know was whether you can test to see if an
object, o, belongs to the class specified by a type variable, T. The
condition (o instanceof T) doesn't seem to work. This was my main
reason for posting here, and it's too bad I picked a bad example for
it. Anyway, I am starting to think that this is impossible. If I'm
wrong, please let me know.

'instanceof' is run-time. Generics are compile-time.

One way, outlined in the Josh Bloch book chapter referenced upthread, is for a
class to keep a 'Class<T>' variable internally to run the check.

Another is to use a helper method with a 'T' argument and a cast its argument
from the caller.

private boolean isFoo( T arg )
{
...
}

public boolean isFoo( Object arg )
{
try
{
@SuppressWarnings( "unchecked" ) // catches CCE
T tA = (T) arg;
return isFoo( tA );
}
catch ( ClassCastException exc )
{
return false;
}
}

Untested, not even compiled.

Casting to a generic type raises a warning, which you can suppress as long as
you check for a 'ClassCastException' or otherwise guarantee the safety.
4. And, yes, I should probably read more of the official
documentation. I didn't this time because I'm under a time crunch.

So not knowing what you're doing makes you faster?
 
M

Mark Space

Greg said:
2. What I really wanted to know was whether you can test to see if an
object, o, belongs to the class specified by a type variable, T. The
condition (o instanceof T) doesn't seem to work. This was my main
reason for posting here, and it's too bad I picked a bad example for
it. Anyway, I am starting to think that this is impossible. If I'm
wrong, please let me know.


As far as I know, you can't do an "instance of T" other than as Lew
suggested -- pass a class literal (object.class) as a type token.

You can however test classes and cast at runtime.

Object o = ...
JButton b = new JButton();
JButton b2 = b.getClass().cast( o );
Class<? extends JButton> c = b.getClass().
asSubclass( JButton.class );

I don't think these are applicable to your current situation. That's
why all the idioms you see as examples you instanceof or other similar
patterns. Actually, I'm not wholly clear on when you would use either of
the above methods (cast() or asSubclass()). Any use I can think of
could be duplicated by a cast ((JButton)) or literal (JButton.class).
I'm sure there are situations though, so I just wanted to point out that
these methods exist.

<http://java.sun.com/javase/6/docs/api/java/lang/Class.html>
 
L

Lew

Greg said:

I get that. It's a fair answer.

I agree if you think this Usenet forum a useful source for down-to-earth Java
knowledge, saving hours or days of sifting through sometimes unhelpful documents.

For what it's worth, the free chapter (5) on generics at
<http://java.sun.com/docs/books/effective/>
is just such a shortcut in its own right. It'll cram your mind with a few,
immediately and deeply useful guidelines about generics within a 30-60 minute
ponder. It gets right to the points you want it to.
 

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,754
Messages
2,569,529
Members
45,001
Latest member
Kendra00E1

Latest Threads

Top