Generics on map.

R

Ravi

Hi,

The following code compiles in eclipse 3.3.1. Shouldn't the compiler
throw an exception as the key doesn't pass instanceof check?

class Test {
public static void main(String args[]) {
Map<String,String> testmap = new HashMap<String, String>();
testmap.get(new StringBuffer());
}
}

Regards,
Ravi.
 
E

Eric Sosman

Ravi said:
Hi,

The following code compiles in eclipse 3.3.1. Shouldn't the compiler
throw an exception as the key doesn't pass instanceof check?

class Test {
public static void main(String args[]) {
Map<String,String> testmap = new HashMap<String, String>();
testmap.get(new StringBuffer());
}
}

No. The get() method of the Map interface takes any
Object reference as its argument, not a parameterized type.
Since testmap will only accept entries that have Strings as
keys, querying for a key of any other type will return null.
It's legal to ask, even when the answer will be "No."
 
M

Mark Space

Eric said:
Ravi said:
Hi,

The following code compiles in eclipse 3.3.1. Shouldn't the compiler
throw an exception as the key doesn't pass instanceof check?

class Test {
public static void main(String args[]) {
Map<String,String> testmap = new HashMap<String, String>();
testmap.get(new StringBuffer());
}
}

No. The get() method of the Map interface takes any
Object reference as its argument, not a parameterized type.
Since testmap will only accept entries that have Strings as
keys, querying for a key of any other type will return null.
It's legal to ask, even when the answer will be "No."

Good one. Reading the OP's example, I assumed that get() took a
parameterized type as well, and I couldn't figure out why an exception
wouldn't be throw.

"When all else fails, read the documentation."
 
R

Ravi

Its quite obvious that runtime return value from the get() is null.
But the very idea of generics is to avoid these runtime mistakes(??)
and push them to design (compile) time. Wont it make sense to throw up
at least a warning that the key is of incompatible type ?? btw, put()
does care about the type check for Key and Value .

Regards,
Ravi.

Ravi said:
The following code compiles in eclipse 3.3.1. Shouldn't the compiler
throw an exception as the key doesn't pass instanceof check?
class Test {
public static void main(String args[]) {
Map<String,String> testmap = new HashMap<String, String>();
testmap.get(new StringBuffer());
}
}

No. The get() method of the Map interface takes any
Object reference as its argument, not a parameterized type.
Since testmap will only accept entries that have Strings as
keys, querying for a key of any other type will return null.
It's legal to ask, even when the answer will be "No."
 
E

Eric Sosman

Mark said:
Eric said:
Ravi said:
Hi,

The following code compiles in eclipse 3.3.1. Shouldn't the compiler
throw an exception as the key doesn't pass instanceof check?

class Test {
public static void main(String args[]) {
Map<String,String> testmap = new HashMap<String, String>();
testmap.get(new StringBuffer());
}
}

No. The get() method of the Map interface takes any
Object reference as its argument, not a parameterized type.
Since testmap will only accept entries that have Strings as
keys, querying for a key of any other type will return null.
It's legal to ask, even when the answer will be "No."

Good one. Reading the OP's example, I assumed that get() took a
parameterized type as well, and I couldn't figure out why an exception
wouldn't be throw.

"When all else fails, read the documentation."

Or as in my case, "Learn from your blunders." I'd tried
to write a Map with case-insensitive Strings as keys:

Map<String,Thing> map = new HashMap<String,Thing>() {
public Thing put(String key, Thing value) {
return super.put(key.toLowerCase(), value);
}
public Thing get(String key) {
return super.get(key.toLowerCase());
}
}

.... and then I got splinters in my fingers from scratching
my head over why it didn't work. After the penny dropped,
I changed the second method to

public Thing get(Object key) {
if (key instanceof String)
key = ((String)key).toLowerCase();
return super.get(key);
}

.... and things worked a whole lot better. (The `if' isn't
necessary, but given my state of confusion at the time it's
perhaps forgivable.) Having been through this, I am now
"Once burned, twice shy."
 
E

Eric Sosman

Putting the response before the stimulus.
What is "top-posting?"
Please don't top-post.
Its quite obvious that runtime return value from the get() is null.
But the very idea of generics is to avoid these runtime mistakes(??)
and push them to design (compile) time. Wont it make sense to throw up
at least a warning that the key is of incompatible type ?? btw, put()
does care about the type check for Key and Value .

Of course put() is parameterized, because it needs to ensure
that you don't insert a String key into a Map that expects only
Integers. But get() doesn't need to be so picky: If you search
that Map for an entry with the key "Mongo", get() reports that
no such entry exists. Nothing odd about that, is there?

Consider this pre-generics situation: I've got a Map

Map squares = new HashMap();

.... and I populate it with some data

for (int n = 0; n < 10; ++n)
squares.put(new Integer(n), new Integer(n*n));

Now I make some queries:

Integer value1 = (Integer)squares.get(new Integer(5));
Integer value2 = (Integer)squares.get(new Integer(42));
Integer value3 = (Integer)squares.get("Mongo");

The first query returns a reference to an Integer with the
value 25. The second returns null because the Map has no
entry with the key 42. And the third returns null because
the Map has no entry with the key "Mongo". What difference
is there between the second and third outcomes? None; they
both declare "key not found," and there's an end on't.

Remember, too, that generics are a compile-time hint and
not a run-time enforcement. By ignoring enough warnings you
can in fact put a String key and a BigDecimal value into a
Map<Integer,Integer> -- and if you were to do so, and then
turn around and search for the String key, shouldn't get()
find the BigDecimal for you?

I imagine that the design decisions might have been made
differently if generics had been part of Java from the start
and if they were backed up by run-time enforcement. But for
whatever reason that's not the way things have turned out.
 
T

thufir

class Test {
public static void main(String args[]) {
Map<String,String> testmap = new HashMap<String, String> ();
testmap.get(new StringBuffer());
}
}


What's a good book to explain this method? Not a beginner's book. Is
this "map" similar to the "map" in ruby?



thanks,

Thufir
 
M

Mark Space

thufir said:
What's a good book to explain this method? Not a beginner's book. Is
this "map" similar to the "map" in ruby?

Well, _Learning Java_ by O'Reilly is a good book that goes into a fair
amount of detail too. It is a beginner's book, but Learning Java has a
huge amount of info that it works well as a reference. Learning Java
has one of the best explanations of generics I've ever seen.

It definitely gets a thumbs up from me.

However, there is also the online Java tutorial from Sun, and don't
ignore Google. Search for "java classname se 6" will take you directly
to the Javadocs in most cases.

<http://java.sun.com/docs/books/tutorial/collections/index.html>

<http://java.sun.com/javase/6/docs/api/java/util/Map.html>

I don't know enough Ruby to tell you if Java maps and Ruby maps are similar.
 
L

Lew

Mark said:
Well, _Learning Java_ by O'Reilly is a good book ...
It definitely gets a thumbs up from me.

However, there is also the online Java tutorial from Sun, ...
<http://java.sun.com/docs/books/tutorial/collections/index.html>

<http://java.sun.com/javase/6/docs/api/java/util/Map.html>

Bird's-eye view of the Java Map, from that link:
public interface Map<K,V>

An object that maps keys to values. A map cannot contain duplicate keys;
each key can map to at most one value.

Some scripting languages, including Java EE's own Expression Language (EL),
depict maps as associative arrays: ${ranches ["BelongaMick"]}.

The "<K, V>" in the interface are the key and value types, respectively. Any
two object types can be associated in a Map.

Map <String, Entity> namedEntities = new HashMap <String, Entity> ();
namedEntities.put( "Mjolnir", new MagicHammerEntity( "Mjolnir", "Thor" ) );
 
R

Roedy Green

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,244
Latest member
cryptotaxsoftware12

Latest Threads

Top