Type Inference: Another Bad Idea for Java 7

Peter von der Ahé and a few others are pushing type inference in Java 7. The goal is to not have to explicitly declare local variable types. Remi Forax offers this example:

  // print a frequency table of words
  public static void main(String[] args) {
    map := new HashMap<String, Integer>();
    for(word : args) {
      freq := map.get(word);
      map.put(word, (freq==null) ? 1 : freq+1);
    }
    System.out.println(map);
  }

Peter von der Ahé and Christian Plesner Hansen suggest reusing the final keyword instead:

  public static void main(String[] args) {
    final map = new HashMap<String, Integer>();
    for(final word : args) {
      final freq=map.get(word);
      map.put(word, (freq==null) ? 1 : freq+1);
    }
    System.out.println(map);
  }

Note that both proposals have the side effect of making the local variable final, as well as inferring its type, although it’s more explicit in the Ahé proposal.

Type inference actually makes some sense in languages like JavaScript and PHP that are built around this, and had this feature from day 1. It makes no sense in a language like Java that’s built around the opposite. It makes Java look weakly typed, but it isn’t. In fact, if anything this is now more strongly typed because, for example, you have to type a Map variable as a HashMap or a TreeMap rather than just a Map. That is,

map := new HashMap();

is equivalent to

HashMap map = new HashMap();

not

Map m = new HashMap();

Much more importantly, though, it’s more syntax that makes the language larger and harder to learn. I used to be able to teach pretty much all the important parts of Java in one semester. Now I can’t even think about doing generics, annotations, and enums unless I drop GUIs. Java 1.0 was designed as a good teaching language. Java 1.1 was pretty good too. Every piece added to it since then—anonymous inner classes, strictfp, closures, enhanced for loop, generics, type inference, etc.—made it less suitable for that purpose.

Even if I try to teach type inference, I have to cover a lot of special cases, and explain what it means to make a local variable final first, and why you might want to do that. This makes some sense to those of us who’ve been breathing this stuff for 10+ years now. It makes no sense at all to anyone who’s coming to the language for the first time. It’s just one more source of weird, incomprehensible compiler error messages they have to ask their professor to debug for them. Is this really worth saving a few keystrokes of typing?

It’s time to call a halt to new features in the Java language. I am not saying that the features are wrong, just that they don’t fit into the language any more. It’s already too bloated. I am not saying that generics, type inference, closures, compiler created factory methods, and other kitchen sink proposals are bad. They’re not. I am saying that they simply don’t fit into or with the current core language, and every one we add makes the language worse, not better.

If these features are necessary, and some of them may be, then they should be designed into a new language from the beginning. This new language could have real generics without type erasure. It could have := but not =. It could have factory methods and not have constructors. It could have closures and not have anonymous inner classes. That would be a language that made sense, and that you could still teach in one semester without tripping over 37 different special cases.

However the current push to add new language features to Java without taking anything away must stop. Every single one makes the language worse, not better. Every single one makes Ruby and C# and Python look more attractive. You cannot build on top of an old foundation forever. Fifty story skyscrapers cannot be built by adding floors to a five-story tenement. Sometimes you have to move down the block and start building in a new lot. For Java, that time has come.

61 Responses to “Type Inference: Another Bad Idea for Java 7”

  1. Augusto Says:

    Thanks for those links, had no ideas these features were being added to C++.

    It would be ironic if C++ gets closures while Java doesn’t …

  2. Ben Says:

    I really don’t like all the JCP proposals to add dynamic language features like type inference and closures by making the language more confusing and totally ignoring the core philosophies behind Java. This one, and all but CICE, make the language more complex and unreadable. If I wanted a language where everyone threw in every fad idea they could think of, I’d be writtting in C++.

    Personally, the feature I’d like to see added is Design-by-Contract similar to how Eiffel does it. I think it would be in line with the core principles of Java and greatly improve readability / robustness of code. Being able to define a contract in the interface and know that every implementor abides by it is simply strengthening current practices. And if it did cause enough of a performance problem (not likely, especially when RoR is the rage and is dog slow) it can be turned off for production but help detect errors early in QA cycles.

    All the features that Java-5 added I use every day, but I rarely find myself wanting any of the ones proposed for Java-7. When I read the blogs of those who propose the additions (like Gafter’s and Colebourne’s) its obvious they’re lost over-designing than dealing with real-world scenarios. I’d rather see Colebourne focus on cleaning up Joda Time for the JDK than screw around with another ugly closure implementation.

  3. emanuel Says:

    Interfaces like Map and List make sense as parameter types for public API method declarations, but not for local or private code/declarations. And also not for public API method return types.

    The statement that the local declaration List list = new ArrayList(); allows to use any kind of list is wrong:

    you have to change the code exactly there where it is used:List list = new LinkedList();
    just look into the JDK collection API documentation how often Sun changes the semantic of overwritten methods. Example: Collection.add(Object) adds element anywhere while List.add(Object) specifies it to append the element. Not to mention that in both cases add(Object) is an optional method, which your code then should not use if it should be able to deal with any List implementation.
    even if the semantic remains the same when switching to a java.util.LinkedList, you will not want to write e.g. for( int i = 0; i but use an iterator instead, because indexing in linked lists is horrible slow.

    Again: using interfaces instead of real classes makes sense for public API but not for local or private code.

    And as shown in the above list in most cases it is even important to know whether some code is dealing with an ArrayList or with a LinkedList. And not only at the location where the variable was initialized.

  4. Michael Nischt Says:

    In principle I like that everything is final by default or the nicer syntax with the special operator ‘:=’. However, I agree it would mess up Java’s style.
    Further, nobody will argue against that:
    final Map<String, Integer> map = new HashMap<String, Integer>();
    is a better coding style than:
    final HashMap<String, Integer> map = new HashMap<String, Integer>();
    Which why I’m also against:
    final map = new HashMap<String, Integer>();
    IMHO, specifying types is not the problem, but double writing for type-paramters sucks. This is why I’d like to see an automatic generation of an overloaded new-method for each constructor:
    final Map<String, Integer> map = HashMap.new();

    Besides, that ‘new’ is a reserved keyword, one could also do that by hand and the type-paramters would be inferred whenever possible. Luckily this makes ‘new’ an ideal candidate for a language addition 🙂

  5. emanuel Says:

    Further, nobody will argue against that:
    final Map map = new HashMap();
    is a better coding style than:
    final HashMap map = new HashMap();

    Well, I just did argue against it. See entry just above yours.
    Here some more reasons: you also don’t write:

    Number n1 = 3;
    Number n2 = 3.7;
    CharSequence s = “hi”;

    If you don’t care about the precise type then use a scripting language, but not Java.

  6. Rob Griffin Says:

    but double writing for type-paramters sucks

    If you use Eclipse then you never need to type the type-parameters more than once; Eclipse’s code completion fills them in for you. I would image (or hope) that other IDEs can do the same.

    At the moment I’m doing a bit of C# which supports duck typing using the var keyword. I don’t like it much because if the variable is being returned by a method you can’t readily see the type.

    E.g.
    var x = getClients();

    What type is x? Also the IDE can no longer go to the definition of the type because var is a keyword.

    In Java I use Eclipse to do the work for me; quick fix can create a variable to hold the return value of a method and guess a name based on the method name e.g. the quick fix ‘Assign statement to new local variable’ on getClients() will result in a variable called clients of the type returned by the method.

    So I think the answer is to use the right tools. No language changes are required .

  7. bohan Says:

    i cannot disagree more with the authors’ resistant point of view. Type inference becomes a necessity to be able to write readable code when parametrized types are used a lot, because type names can become very long and tedious to read. Ever wrote loops that iterate over parametrized containers in C++? I wish C++ would introduce type inference in standard too (it has typedef, which can reduce type name bloat, but it’s not as nice as inference, and typeof is not standard and not as nice either)

  8. Carlos Says:

    > Type inference actually makes some sense in languages like JavaScript and PHP that are built around this, and had this feature from day 1

    Oh, my friend! you have no idea what type inference is for an statically typed language…

    http://en.wikipedia.org/wiki/Type_inference

    However, java is not 100% statically typed – presents itself like that, but it isn’t – and in this sense similar to PHP.

    I recomend for anyone reading this to look for Haskell or Concurrent Clean to see what type inference mean (or Scala, which already has it and runs on the JVM)

  9. Barney Says:

    Isn’t everyone missing the point? If we want to reduce clutter and keystrokes, It is the RHS which should be inferred, not the LHS! Never understood why Java didn’t support C++ style instantiation from day 1 – the new operator is completely unnecessary in Java.

    /* StringBuilder builder=new StringBuilder(64); */

    StringBuilder builder(64);

    /* LinkedHashMap<String, ArrayList> map=new LinkedHashMap<String, ArrayList>(); */

    LinkedHashMap<String, ArrayList> map();

    Seldom is there any benefit in declaring a variable as the interface rather than the concrete type we know we will instantiate it to, and in these rare cases the existing syntax will do fine.

  10. tiny tim Says:

    Hogwash… Who is forcing you to use language features you don’t want to?

    Quite a number of strongly typed languages support type inferencing, join the rest of us in 2010 when you catch the clue train!

    That said… I don’t see why we need this: x := “duh” , business, x = “duh” should be sufficient.

    Explicitly asking for type inferencing is the sort of goofy crap that led to the orginal bloat this sort of feature was meant to combat in the first place!

    Type inferencing should be done at compile time (hello javac, g++ calling) not at runtime.

  11. Anonymous Says:

    1. Misunderstand proposed language feature.
    2. Get angry about how you don’t like it.
    3. Complain that Java already has too much different syntax anyway.
    4. End in praise of C#, which is suffering from a much more severe case of syntax cruft.
    5. ???
    6. Profit