Ever since I heard of type inference (in Haskell), I lived under the impression that Java is the exact opposite, i.e., it has no type inference. Recently though, I had an aha moment and realized that Java employs type inference in its generics implementation.
Then, I read two papers by Gilad Bracha (one of the people behind the generics implementation in Java, as far as I understand). The first paper is a tutorial about generics (PDF), in which he explicitly says that the compiler will infer the actual type argument used to replace the format type parameter. So, there's type inference in Java, but why only for generics, why not something like C#'s var
keyword? And this is my question to you.
Why doesn't Java has more type inference built into the compiler?
I will suggest an answer though, and this is related to the second paper that I read, Pluggable Type Systems (PDF). It seems, Gilad Bracha believes that the inference part shouldn't be part of the compiler, but an IDE feature or similar (section 4, paragraph 6 in the above mentioned paper):
A better engineering approach is to implement type inference as a separate tool, available in the IDE. Programmers who find entering type annotations tiresome can invoke an inferencer on demand.
What do you think?
Well, I think type inference is in Java for historical reasons mostly: as befits a language with strong legacy constraints, Java improvements are made cautiously & incrementally (as the JCP shows, even though some improvements of type inference manage to go through). With generics, the long-standing GJ implementation was thoroughly evaluated before inclusion in Java 5.
Prior to the release of Java 5, there was no type inference in Java. (...) When generics (...) were introduced in Java 5, the language retained this requirement for variables, methods, and allocations. But the introduction of polymorphic methods (parameterized by type) dictated that either (i) the programmer provide the method type arguments at every polymorphic method call site or (ii) the language support the inference of method type arguments. To avoid creating an additional clerical burden for programmers, the designers of Java 5 elected to perform type inference to determine the type arguments for polymorphic method calls. (source)
But that does not mean that there is a strong culture for pervasive type inference in Java. As per the spec:
Note also that type inference does not affect soundness in any way. If the types inferred are nonsensical, the invocation will yield a type error. The type inference algorithm should be viewed as a heuristic, designed to perform well in practice. If it fails to infer the desired result, explicit type parameters may be used instead.
I do think more type inference for Java would be a boon (Scala is already a very interesting improvement in that direction). IMHO, type inference makes the feedback loop with the type checker less mechanical, while being just as sound, letting you write less types, but making you type-check just as much. Since a major benefit of types is to direct the mental process of program search ("letting you write within the space of well-typed programs, rather than in the space of ascii turds"), this comfort in interaction with the type checker seems invaluable : you get to have a type-checker that verifies you think in well-typed terms and trains you to do so, rather than making you account for it on every line.
Now, the stage at which type inference should happen is another question. I think wanting to have "inferencers" separate from the runtime answers legacy concerns : it avoids requiring you to have a type inference algorithm that is always backwards-compatible. But the key then becomes what your standard/major libraries look like : is the source you publish & exchange with others annotated or not ?
While it's true that an annotated source can, valuably, be type-checked whatever the strength of your inference engine is, I'd still want a type inferencer in the compiler, because it's not only that I don't want to write List<CacheDecoratorFactory> cacheDecoratorFactories = new ArrayList<CacheDecoratorFactory>();
, it's that I don't event want to read it. Nor do I want to deal with it when I refactor pre-existing source, for that matter. I would need a type "hider" erasing annotations before I interact with source, but if the type inference engine is not complete, the problem of which annotations to erase, and ensuring the erasure followed by type reconstruction is bijective becomes thorny (esp. if your inference engine doesn't return a principal type) ... If we have to solve a thorny problem anyway, why not make it a good, and as-complete-as-possible type inference algorithm ? My hunch is that past a certain level of quality (particularly in the generality of the returned types), the legacy concerns would start to fade.
The type inference is available in IntelliJ, possibly other IDEs as well. You can write an expression (or use an existing one) and select "Introduce Field/Local variable/Constant" etc and it will give you some type options inferred and some suggested names. If the expression appears more than once it gives you the option to replace all occurrences. e.g. say I have a string I want to turn into a parameter
myMethod();
public void myMethod() {
"/tmp/20101112/data.file"
}
I select the date portion and <ctrl>+<alt>+P and it suggests an int type to add as a parameter. It will inline this date into all callers.
myMethod(20101112);
public void myMethod(int date) {
"/tmp/"+date+"/data.file"
}
I place "new FileInputStream(" at the start and introduce a Local Variable. <ctrl>+<alt>+V
FileInputStream fileInputStream = new FileInputStream("/tmp/"+date+"/data.file");
It highlights that this can throw an exception which I can auto fix a number of ways. I select <alt>+<enter> and add the exception to the throws clause of the method.
myMethod(20101112);
public void myMethod(int date) throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("/tmp/"+date+"/data.file");
IMHO, it makes much more sense to have the IDE do the work as it can do much more interactively than the compiler and you can see explicitly what your types become.
This isn't really an answer, but on a side note, you might want to look into the D language. It allows you to write code like this:
int*[6]*[wstring][]*[string]*[] myVar;
auto myVar2 = new typeof(myVar[0])[100]; // equivalent to: new int*[6]*[wstring][]*[string]*[]*[string]*[100]
Basically, it's manual inference + auto-inference, and it lets you write very generic code that's more difficult to write with other languages. (The example here isn't very realistic, but it illustrates the point.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With