Notes on implementing concise calls to constructors with type parameters

Reinier Zwitserloot reinier at zwitserloot.com
Wed May 13 17:28:02 PDT 2009


Here's a crazy idea:

Why don't we deprecate raw types for constructors?


I know what you're saying: Deprecate stuff? in the core? we don't do  
that in java land!


But I think it'll work out fine. All this means is:

A constructor call with no generics information, on a type where  
generics information is expected, is no longer the raw type. It is the  
'infer me please' type, entirely analogous to the diamond notation.

Here's the trick, though: This inference system can infer 'raw', no  
problem. The following code:

List foo = new ArrayList();

has a raw List on the LHS (no changes in the type system, just  
constructor calls), and thus the RHS would infer to new  
ArrayList<RAW>().

The following:

List<String> foo = new ArrayList();

currently results in a raw list on the RHS, then raw-to-generified  
conversion (which gives you a warning) to make the ArrayList<RAW> fit  
in the required List<String> type. After this change, this would work  
differently: the RHS is now ArrayList with the notion that the  
generics need to be inferred, just like they would for static methods.  
In this case, String is inferred, and the warning goes away.


This boils down to:


  - no code that used to compile fine will now compile with errors or  
warnings.
  - warnings that used to exist will either still exist, or go away,  
or become a different warning.


It's not 100% backwards compatible though, there's this one extremely  
rare case where you run into trouble. Behold this monstrosity:

import java.util.List;
import java.util.ArrayList;

public class Foo {
	public static void overloaded(Object x) {
		System.out.println("overloaded(Object) called.");
	}
	
	public static void overloaded(Number x) {
		System.out.println("overloaded(Number) called.");
	}
	
	public static void main(String[] args) {
		overloaded(getList().get(0));
		overloaded(new TestList().get(0));
	}
	
         //these methods are ugly but that's because they are  
contrived use cases.
	@SuppressWarnings("unchecked")
	public static <T extends Number> List<T> getList() {
		List x = new ArrayList();
		x.add(1);
		return x;
	}
	
	@SuppressWarnings("unchecked")
	public static class TestList<T extends Number> extends ArrayList<T> {{
		List x = this;
		x.add(1);
	}}
}


The raw type of TestList will end up calling the Object version, but  
the generics inference of the static method infers Number, due to the  
type bound.

Showstopper? No problem. Just add a rule that says: If the constructor  
expression is used in a place where the inference engine cannot make  
an unambiguous decision, instead of inferring the lower bound of the  
generics parameter (the Foo in extends Foo), always infer Object.

I find the empty diamond notation very ugly; feels like a hack just to  
ensure backwards compatibility. I think we can do better.

  --Reinier Zwitserloot



On May 13, 2009, at 19:58, Ulf Zibis wrote:

> BTW 1 question:
>
> Is there any difference in writing:
>    List<String> l = new ArrayList();
> or:
>    List<String> l = new ArrayList<String>();
>
> I guess not, so why using 2nd writing.
>
> thanks,
>
> Ulf
>
>
>




More information about the coin-dev mailing list