> The first case is a corner case and for generics, it works until someone try 
> to stuff null into a value type.
> 
> So instead introducing nullable value types in the language which make the 
> language far more complex than it should be, i think we should come up with a 
> far simpler proposal, to have a declaration site tagging of the method that 
> doesn't work with value types. 
> 
> 
>  // proposed syntax
>  interface Map<K, V> {
>    "this method doesn't work if V is a value type" public V get(Object o);
>  }

We explored this idea in M3; we jokingly called this “#ifref”, which is to say, 
we would restrict members to the reference specialization.  This was our first 
attempt at dealing with this problem.  We gave up on this for a number of 
reasons, not least of which was that it really started to fall apart when you 
had more than one type variable.  But it was a hack, and only filtered out the 
otherwise-unavoidable NPEs.  

More generally, there’s lots of generic code out here that assumes that null is 
a member of the value set of any type variable T, that you can stuff nulls in 
arrays of T, etc.  Allowing users to instantiate arbitrary generics with values 
and hope for no NPEs (or expect authors of all those libraries to audit and 
annotate their libraries) is not going to leave developers with a feeling of 
safety and stability. 

Further, allowing users to instantiate an ArrayList<Point> — even when the 
author of ArrayList proposes up and down (including on behalf of all their 
subtypes!) that it won’t stuff a null into T — will cause code to silently 
change its behavior (and maybe its descriptor) when ArrayList is later 
specialized.  This puts pressure on our migration story; we want the migration 
of ArrayList to be compatible, and that means that things don’t subtly break 
when you recompile them.  Using ArrayList<V?> today means that even when 
ArrayList is specialized, this source utterance won’t change its semantics.  

Essentially, erased generics type variables have an implicit bound of “T 
extends Nullable”; the migration from erased to specialized is what allows the 
declaration to drop the implicit bound, and have the compiler type-check the 
validity of it.  

We have four choices here:

 - Don’t allow erased generics to be instantiated with values at all.  This 
sucks so badly we won’t even discuss it.  
 - Require generics to certify their value-readiness, which means that their 
type parameters are non nullable.  This risks degenerating into the first, and 
will be a significant impediment to the use and adoption of values.  
 - Let users instantiate erased generics with values, and let them blow up when 
the inevitable null comes along.  That’s what you’re proposing.  
 - Bring nullity into the type system, so that we can accurately enforce the 
implicit constraint of today’s erased generics.  That’s what I’m proposing.  


I sympathize with your concern that this is adding a lot of complexity.  
Ultimately, though, I don’t think just letting people blindly instantiate 
generics that can’t be proven to conform to their bounds is not helping users 
either.  Better suggestions welcome! 

(A related concern is that V? looks too much like ? extends V, especially in 
the face of multiple tvars: Map<V?, ? extends U>. This may have a syntactic 
solution.). 

 

Reply via email to