I've always found it quite bothering that trying to access a missing key in a Dictionary in .Net throws an Exception rather than returning null. Being so used to JavaScript dictionaries (and to Perl hashes now), where it will just return undefined/undef, I sometimes forget about the difference in .Net and miss using ContainsKey or TryGetValue, ending up with an Exception.
If we think about why the .Net designers took this decision, I think the explanation comes from the Reference Type/Value Type dichotomy. In a Dictionary<K,V>, V can be a Reference type or a Value type. For Reference types, if the key is not present we could return null without a problem, but this is not the case for Value types, as Value types can't be null. One solution would be having applied the same logic as in TryGetValue, and return the default for that type (0 if it's a number). I don't think that would be a good idea, cause you would not know then if the item was in the Dictionary or not. I think this behaviour is fine for TryGetValue, as it's not the standard way to access an element, but not for the the most common accessor to a Dictionary elements.
This has led me to think about how Java behaves for similar data structures. Looking at the Hashmap and HashTable documentation for the get method (yeah, not even in its almighty Java 8 does Java feature indexes... ) we can see that:
Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
OK, sure that's cool but, how do they handle Value types (the 8 primitive types supported by Java)? After writing a few lines of code to check this and failing to compile it I realised that Java Generics do not support Primitive Types. Sure it's a serious limitation (caused I guess by the same "don't break previous code" aim that led them to choose their non reified flavour of generics), but for this case it gives you a small feature.
So this code in Java won't crash:
Integer numObj; int numPrimitive; Hashtable<String, Integer> myHashTable = new Hashtable<String, Integer>(); myHashTable.put("Haute-Garonne", 31); numObj = myHashTable.get("Haute-Garonne"); System.out.println("Haute-Garonne: " + numObj); numObj = myHashTable.get("Gironde"); //returns null System.out.println("Gironde: " + numObj); //works fine
but notice that obiously, trying to unbox will throw a NullPointerException:
try{ numPrimitive = numObj; //NullPointerException when trying to unbox a null object into an int } catch(Exception ex){ System.out.println("Exception: " + ex); }
There's something rather curious here, not the fact of throwing an exception, that is pretty logical, but the name of the Exception. NullPointerException. The usage of the term "Pointer" in Java seems pretty weird to me. It looks to me that calling it NullReferenceException as .Net does would be quite more appropriate. Seems like I'm not the only one puzzled by such naming decision. Someone justifies it by the fact that pointers and references are indeed the same, the memory address of the object. I would say such a justification is quite wrong. In .Net and Java (and I guess almost all platforms) the fact that a reference holds the address of the referenced object is an implementation detail. Rather than that, a reference could just hold an ID for an entry in a table that would in turn point to the objects (something similar to Object Handles in Windows OS), and such implementation should be transparent to the programmer.
One could wonder why Reference Types are not nullable. I don't fully agree with the accepted answer here. I see it more in terms of how would you differentiate null from a valid value. I mean, I think both Java and .Net internally represent null with a 0. So, how would you distinguish an int or a float which value is 0 from one with no value (null)?
Related to this, the question of how JavaScript implementations store null and undefined comes to mind. After a fast search I haven't found an answer, I think I'll look deeper once I have a chance.
No comments:
Post a Comment