Java Forum / General / January 2006
generic oversight?
VisionSet - 30 Dec 2005 19:47 GMT When they retro fit generics to Collection classes why didn't they change methods like this
containsKey(Object key)
to
containsKey(K key)
I was in secure type safe heaven for a bit...
-- Mike W
Thomas Hawtin - 30 Dec 2005 20:37 GMT > When they retro fit generics to Collection classes why didn't they change > methods like this [quoted text clipped - 6 lines] > > I was in secure type safe heaven for a bit... On the face of it, it doesn't look type unsafe, does it? I mean if you give me a HashMap with a String key and I look for an Integer in it, I just wont find any (unless it's a null reference, possibly).
If I take you Map<String,Void> and assign it to Map<?,Void>, that should work. I've forgotten the type of the key, but really is there any problem with me looking up a key in it? And in order to do that, your contains key needs to be more relaxed. Something like:
boolean containsKey(? super K key); // NOT LEGAL
That's not legal. Although if it was, appropriate semantics could make it illegal to check whether a Map<String,Void> contains an Integer key. The nearest we can get to the above is:
boolean containsKey(Object key);
TreeMap introduces complications because the Comparator/Comparable may throw a ClassCastException. If we wanted to check the comparator, then the Map interface would need to develop a third generic parameter, that would almost always be irrelevant. Alternatively, I guess TreeMaps could do a preliminary check, but unfortunately the never did so can't really if backward compatibility is to be maintained (and they don't have the K.class).
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
ricky.clarkson@gmail.com - 31 Dec 2005 13:01 GMT Mike,
For this (and other) reasons I implemented my own collection wrappers, which wrap Collection, List, etc., but have more sane API, including contains(T). This isn't currently public, because I'm lazy, but it could be.
You can either suggest that it should be public, which might kickstart me to making it so, or implement your own. However, I doubt Sun will, because they're afraid to stop old code from compiling.
Cheers.
VisionSet - 31 Dec 2005 13:23 GMT > Mike, > [quoted text clipped - 6 lines] > me to making it so, or implement your own. However, I doubt Sun will, > because they're afraid to stop old code from compiling. Thanks Ricky, it's enough to know I'm not going mad and missed something. I'm aware now to not assume a class I know is Generified is not necessarily completely so. Would be a nice feature for serious use though.
-- Mike W
Thomas Hawtin - 31 Dec 2005 13:50 GMT > For this (and other) reasons I implemented my own collection wrappers, > which wrap Collection, List, etc., but have more sane API, including > contains(T). This isn't currently public, because I'm lazy, but it > could be. The collections are fundamentally part of the Java language. If I see code using collections, then I expect it to be all standard stuff. No need to look anything up. There is a high price to pay for going non-standard.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
ricky.clarkson@gmail.com - 02 Jan 2006 00:13 GMT I disagree. There is a high price for bugs. Writing/using a separate library is easy.
If having ipsim.util.List and java.util.List confuses you, call it something else, as ContractualJ ( www.contractualj.com ) does (Sequence and other names).
I found keeping the names the same made it easy to adapt existing code - most of the time I just changed the imports. It would be trivial to refactor now I suppose, but I don't see the point.
If you really believe that using something that is non-standard is harmful, then it will be difficult to make progress.
Apologies to all if this is a repeated post, I got mildly confused.
Thomas Hawtin - 02 Jan 2006 11:45 GMT > I disagree. There is a high price for bugs. Writing/using a separate > library is easy. How many bugs have you had from containsKey taking Object? How many have other people had?
> If having ipsim.util.List and java.util.List confuses you, call it > something else, as ContractualJ ( www.contractualj.com ) does (Sequence > and other names). List.contains is a poorer example than Map.containsKey. I don't know of any implementation of List.contains that throws ClassCastException. A non-List Collection from TreeMap/TreeSet may.
> I found keeping the names the same made it easy to adapt existing code > - most of the time I just changed the imports. It would be trivial to > refactor now I suppose, but I don't see the point. Deliberately clashing names with common, standard types is something I am heavily against. See Josh Bloch's Effective Java. At least do the Swing thing and add an initial initial.
> If you really believe that using something that is non-standard is > harmful, then it will be difficult to make progress. Making arbitrary changes to mature, tested and very well established libraries, designed by some quite clever people, is not progress. Progress is 10% innovation and 90% standardisation (I just made that up, if you can't tell). Standing on the shoulders of giants, that sort of thing.
If you have a contempt for standardisation and want to avoid NPEs and CCEs, then write a new language rather than corrupt an existing one.
To stay in Java and avoid these CCEs, write wrappers that check types. The wrappers can implement the standard interfaces, so there are only small changes necessary for conventional code. The problem is so obscure that Collections.checkedMap and friends don't bother with it.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
ricky.clarkson@gmail.com - 02 Jan 2006 19:17 GMT > How many bugs have you had from containsKey taking Object? How many have > other people had? I think I've said this elsewhere recently, so sorry if you're reading this twice:
I had various things like List<ComponentHandler> and decided to change them to List<NetworkComponent>. I could do most of this by looking at the compile errors, fairly easy to go through my codebase and fix. Then I started testing, and kept finding cases where I was doing componentList.indexOf(handler), which will always return false.
So I changed to using ipsim.util.List and the compiler picked up the errors.
I can't accurately vouch for other people, but surely if there was no problem then the OP wouldn't have posted.
I don't know what you mean with ClassCastException. I don't want ClassCastException, I want compile errors.
> Deliberately clashing names with common, standard types is something I > am heavily against. See Josh Bloch's Effective Java. At least do the > Swing thing and add an initial initial. I will see that book sometime. Meanwhile, I don't see it as a problem because I am *replacing* java.util.List in my code. I don't want to even consider using both together. Plus, we have packages, so there already are separate namespaces.
> Making arbitrary changes to mature, tested and very well established > libraries, designed by some quite clever people, is not progress. I still use the same implementations, I respect the efforts made to make ArrayList grow smoothly, etc., but I just want a slightly different interface, so I wrapped it.
I am allowed to disagree with these 'clever people'.
> If you have a contempt for standardisation and want to avoid NPEs and > CCEs, then write a new language rather than corrupt an existing one. I'm not corrupting anything. I am making new APIs. I believe Java is flexible enough to support experimentation into APIs without having to involve a new programming language at every turn. On the other hand it is also too flexible without code checking tools for many people's requirements.
I don't dislike standardisation, but I don't see why it should be done arbitrarily. There should be reasons for things that are in standards. I haven't seen a reason from anyone 'quite clever' yet that explains why contains is not generic.
Chris Smith - 03 Jan 2006 18:35 GMT > I don't dislike standardisation, but I don't see why it should be done > arbitrarily. There should be reasons for things that are in standards. > I haven't seen a reason from anyone 'quite clever' yet that explains > why contains is not generic. Thomas gave you that answer on Friday, at 1:45 PM in my local time. You responded. If you weren't observant enough to see it, that's not anyone else's fault.
Here's the reason again, in slightly different terms. If List<T> had a contains(T) method rather than a contains(Object) method, then the following code would become illegal:
List<?> someList = something.getList(); Object key = something.getKey();
if (someList.contains(key)) System.out.println("Hello!");
This should be legal code. I should be able to check if the list contains the object. I don't need to know the element type for someList (the type parameter) in order to test for membership. Forcing me to do so may force me to introduce unnecessary dependencies of more stable code on less stable code, which causes problems with maintenance.
There's a broader point here, though, which Thomas also made. A group of really smart people led the development of the Collections API. They didn't make dumb mistakes. There may have been better design choices, but they weren't stupid and they weren't careless. You showed up on a public discussion forum, claimed that their design choices aren't even sane. You suggested that other people should abandon use of the Collections API, and instead wrap it with their own classes that make ill-considered changes to the API, which you decided upon on your own, without any consultation with other people, without forming a group of experts like the ones involved in designing the Collections API.
Then you tricked people, who -- like you -- don't understand the problems involved, into doing something that's not smart.
In short, you could stand to learn some humility. Thomas's points about standardization all come down to that (plus some very important practical issues that I might explain in a later post... but that ought to be enough).
 Signature www.designacourse.com The Easiest Way To Train Anyone... Anywhere.
Chris Smith - Lead Software Developer/Technical Trainer MindIQ Corporation
Chris Smith - 03 Jan 2006 19:06 GMT > > Deliberately clashing names with common, standard types is something I > > am heavily against. See Josh Bloch's Effective Java. At least do the > > Swing thing and add an initial initial. > > I will see that book sometime. A good time to read Josh's very excellent book might be BEFORE you start giving advice on newsgroups that assumes that Joshua Bloch is an idiot.
> Meanwhile, I don't see it as a problem > because I am *replacing* java.util.List in my code. I don't want to > even consider using both together. Hope you don't have to interact with anyone else's code, then. They are likely to give you standard collections. You're going to spend a whole heap of time communicating between the two.
In general, I'd say that anything that prevents you from (or makes difficult) using existing solutions to difficult problems is a bad thing. This is one of the important reasons that standard collections came about in the first place. People communicate collections between different pieces of code.
> I am allowed to disagree with these 'clever people'. Not until you understand both the reasons for their decisions, and the consequences of breaking them. Even an extremely poor standard object that can be used for communication between several modules of code is often better than a perfect non-standard implementation. Since you are providing far from perfect alternative, you have quite a bar to clear before it becomes worth making the change.
Again, I have no problem with you doing something dumb. I have a problem with you convincing others to do something dumb.
 Signature www.designacourse.com The Easiest Way To Train Anyone... Anywhere.
Chris Smith - Lead Software Developer/Technical Trainer MindIQ Corporation
ricky.clarkson@gmail.com - 04 Jan 2006 04:56 GMT Chris,
Quite a love affair this is.
List<?> someList = something.getList(); If you make that into List<? extends Object> then you could use contains(new Object()) on it, I think, with my amazingly dumb API. I haven't tested this. I don't use ? for anything myself.
Regarding sanity, someone famous once said something along the lines of "wars start because everyone believes everyone else is a little bit insane". Point taken though, I should be a little more careful with my choice of words. It wasn't meant as an insult to any person or organisation.
As a 'full' user of generics, as far as I know, I don't need the ? notation, so the use case of wanting to look for an arbitrary object in a List<?> doesn't arise for me. I don't know when you might want a List<?>, so given that exception, maybe I made a mistake.
A possibility without doing a lot to existing code would be something like:
final class CollectionUtility { public static <T> boolean contains(T object,List<T> list) { return list.contains(object); } }
I also implemented read-only interfaces, e.g., ListView, MapView, and used it partially to eliminate bugs and partially as an experiment into how I would go about implementing collections from scratch myself. That, I think, gave me a better understanding of generics, and a better understanding of the existing implementations. I think both of these goals are fine.
My new interfaces might not be useful with the ? notation, but I don't need them to be. That's ok. I haven't published them, only the idea of them, and even if I did, it would not be a bad thing. The original poster, for example, might try out my interfaces, or just read about them, with the comment that they won't work well for List<?>.contains(Object), and decide against using them. That's fine, it's good, the OP would have been educated.
I firmly believe it's better to try new ideas than to unknowingly stick with old ideas.
> A good time to read Josh's very excellent book might be BEFORE you start > giving advice on newsgroups that assumes that Joshua Bloch is an idiot. I did not say ANYTHING against Joshua Bloch. Kindly try not to put words in my mouth. I knew he had something to do with the development of Java's enums; I didn't have a clue he was involved in generics too.
> I have a > problem with you convincing others to do something dumb. That's just one of the advantages of a public forum, isn't it. I can suggest something, you can shoot it down, hopefully with good arguments that make the OP able to make an informed decision. I could have emailed him privately with a jar file full of these interfaces, but I didn't. You'll kindly note that I didn't force my ideas on the OP. I told him what I did, and what he could do if he wanted to follow the same route. Again, my language could have been better. I'm fairly new to newsgroups.
Cheers.
Chris Smith - 04 Jan 2006 06:29 GMT > List<?> someList = something.getList(); > If you make that into List<? extends Object> then you could use > contains(new Object()) on it, I think, with my amazingly dumb API. I > haven't tested this. I don't use ? for anything myself. No, that's not true. You'd have to use List<? super Object>, which is of course equivalent to List<Object>.
If you don't use wildcards for generics, how do you avoid spending massive amounts of time copying generic data structures from one to another? Do you never store objects that belong to interesting inheritance trees in generic collections?
> As a 'full' user of generics, as far as I know, I don't need the ? > notation, so the use case of wanting to look for an arbitrary object in > a List<?> doesn't arise for me. I don't know when you might want a > List<?>, so given that exception, maybe I made a mistake. You might want a wildcard List if implementing any generic utility method designed to work with Collections. Generally you do want some bounds. For example:
public <T> List<T> concat(List<? extends T> a, List<? extends T> b) { List<T> result = new LinkedList<T>();
result.addAll(a); result.addAll(b);
return result; }
...
List<Dog> d = new LinkedList<Dog>(); List<Cat> c = new LinkedList<Cat>();
List<Animal> a = concat(d, c);
It's more complex to create a realistic situation in which case you might want a wildcard list with no bounds at all. It generally involves interoperating with external code at some significant level of detail. But it does happen.
> final class CollectionUtility > { [quoted text clipped - 3 lines] > } > } The method you provided can only be used with a List<? super X> (or a List<X>, of course) for some X that is a supertype of the type of the first parameter. In other words, it doesn't accomplish anything more than your original proposal for List.contains(T).
> I also implemented read-only interfaces, e.g., ListView, MapView, and > used it partially to eliminate bugs and partially as an experiment into > how I would go about implementing collections from scratch myself. There are, of course, Collections.unmodifiableList, Collections.unmodifiableMap, and Collections.unmodifiableSet. If you don't like the decision to use optional operations, see the file called designfaq.html in the docs/guide/collections directory of any Java 1.5 SDK installation. There are very good reasons given there for making that decision... some empirical and some logical in nature.
That said, I'm surprised that you implemented these views and still haven't used wildcards. That seems rather difficult to believe.
> My new interfaces might not be useful with the ? notation, but I don't > need them to be. That's ok. The need for wildcards in generics is not limited to working with other code that uses wildcards. If you wrote code that doesn't use wildcards, it won't work well with other perfectly good code that does NOT use wildcards. Wildcards turn out to be logically necessary in the Java generics type system.
 Signature www.designacourse.com The Easiest Way To Train Anyone... Anywhere.
Chris Smith - Lead Software Developer/Technical Trainer MindIQ Corporation
Chris Uppal - 04 Jan 2006 11:20 GMT > When they retro fit generics to Collection classes why didn't they change > methods like this [quoted text clipped - 4 lines] > > containsKey(K key) I'm not sure if anyone has actually answered this...
First off, they couldn't /replace/ containsKey(Object) with containsKey(K) since that would break backward compatibility with code that expected the former method to exist. Secondly they couldn't just /add/ containsKey(K) since that would not necessarily be distinct from containsKey(Object) -- K might be Object. Lastly they couldn't use hacky magic like that which allows, say, String to implement Comparable<String> with a method
int compareTo(String)
which magically implements the Comparable (i.e. post-erasure) interface's requirement for a method:
int compareTo(Object)
because that's done by synthesising a method:
int compareTo(Object o) { return compareTo((String)o); }
Using an equivalent hack in this case would either result in a containsKey(Object) method which threw class-cast exceptions rather than answering false (thus breaking the previously established contract), or would require the compiler to "know" somehow that the synthetic bridge method should read:
boolean containsKey(Object o) { return (o instanceOf K) ? containsKey((K)o) : false; }
which is impossible for at least two reasons...
-- chris
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|