Static Factory Methods


Let me jump right into it. I propose an alternative to type inference for local variables. I'll explain why later. Everybody knows this example:

Map<String,List<Integer>> map = new HashMap<String,List<Integer>>();

I propose this solution:

Map<String,List<Integer>> map = HashMap.new();

Before I get to caught up in the details, let me examine why we are talking about this in the first place. As far as I'm aware, there are primarily two reasons for looking at examples as the above:

  1. DRY—Don't repeat yourself.
  2. Inspiration from scripting languages (or dynamically typed languages): JavaScript, Ruby, Smalltalk, Scheme, etc. Moving to Java from such languages can feel very constraining because you have to type so many types.

I'm currently aware of two serious proposals, the first from James:

map := new HashMap<String,List<Integer>>();

The second is from Christian:

final map = new HashMap<String,List<Integer>>();

Rémi has implemented both of these proposals. But let us not forget what is going on in other languages:

var map = new HashMap<String,List<Integer>>();

This is well known from JavaScript and has recently been added to C#. However, adding it to Java would require adding a new keyword (which is bad) or having some really strange rules. So var is not really a contender for Java.

All of these proposals (mine excluded) essentially solve the problem using the same technique: type inference. The problem is that this promotes bad behavior:

  • Forces use of class types, not interface types on local variables. Generally the opposite is recommend as implementations are more flexible when they rely on interfaces instead of specific implementation classes. This is mediated by only allowing this syntax on local variables so no API is affected.
  • Code could be slightly harder to read. In many cases type declarations on variables are helpful when reading some code. Most will argue that some of the really nasty examples with long type arguments are fun to neither read nor write. So eliding the type on a variable can sometimes improve readability. However, since the language can't enforce a readability rule, some developers will misuse such a feature.

This motivated Neal to suggest:

Map<String,List<Integer>> map = new HashMap<>();

Neal and I just had a chat about this. I think we both agree that it is ugly but the alternative could have compatibility problems (as well as other problems):

Map<String,List<Integer>> map = new HashMap();

So you can probably see how I got the idea:

Map<String,List<Integer>> map = HashMap.new();

However, there is more to it than just a simple syntax for constructing instances. My initial reaction to this problem was: we need more static factories throughout the JDK. For example:

Map<String,List<Integer>> map = HashMap.create();

Where the definition of create is:

public static <K,V> HashMap<K,V> create() { return new HashMap<K,V>(); }

What I propose is actually a new syntax for declaring static factory methods and that the compiler adds them by default (as it does with constructors). For example, if you declare a class:

public class Box<T> {
    public Box() {}
}

The compiler would automatically add this method:

public static <T> Box<T> new() { return new Box<T>(); }

The programmer can specify any number of new methods just as it is possible to have multiple constructors. If the programmer provides a new method with a signature that matches that of a public constructor, that will be used instead of the compiler providing one.

Thanks to Neal M Gafter for his input on this idea and this posting.

Comments:

Post a Comment:
Comments are closed for this entry.
About

The Former Weblog of Peter Ahé

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today