The debate between static and dynamic typing is a long one, and not one I have a definite opinion on. I tend to jump from one side to the other depending on the phase of the moon. I am going, however, to make a statement now that is entirely true:
Java's implementation of static typing is stupid.
Before anyone jumps down my throat, and because "stupid" is also a general-purpose perjorative, I'm going to give a more formal definition of what it is to be a stupid type system:
A stupid type system is one where the programmer is forced to do unnecessary work for the compiler.
A good way to get an idea of how stupid Java's type system is, is to use a modern Java IDE for a while. Start using IDEA's intention actions, and see just how often IDEA knows what the type of an object is going to be before you bother to assign it a type, or make a cast. Every time you ask the IDE to assign a cast, create a field or iterate over a collection and you select the default value, you're telling the compiler something that IDEA was smart enough to know already.
And with 1.5 and the introduction of generics, the type system is getting even stupider, giving you more and more places where you have to tell the compiler what types to expect because it's too stupid to work it out for itself. Even though the IDEs are smart enough to do this, it's still annoying and disruptive for the programmer to have to make the context switch and alt-enter a cast into place. I'd much prefer Java itself was better able to this sort of stuff itself.
I'd love to see Java have some form of type inference. Because of the way Java treats separate classes as independent compilation units, you'd have to keep interactions between classes manifestly typed. But within a class, there's no excuse for the compiler not to be able to infer the type of variables and method signatures from the objects that are assigned to them, and the methods that are called on them.
You wouldn't lose any type-safety. We are not removing static typing, we're just leaving it to the compiler to fill in the obvious bits. The compiler would still be perfectly able to spot the assignment of incompatible classes to a variable, or the calling of non-existent methods on an object. It could even detect when you were moving incompatible objects in and out of a collection without any tedious mucking around with expressly declared generics.1
The only ambiguity would come from methods that are overridden with arguments of overlapping types, which is really bad practice anyway.
All you'd lose is, well, a lot of wasted typing.
See also:
- Sam Pullara: Usability and Java 1.5 generics vs. Autocasting
- MJD: Strong Typing in Perl
(This post originally came with a code example which I wrote while also trying to wrestle with CruiseControl. As a result, I quickly removed the example because it was woefully incorrect in almost every respect, and I've already wasted enough time today writing this post. On the other hand, I fixed the CruiseControl build by changing the max size of the permanent generation. Hooray.)
1You'd still need generics so that you'd know you expect the same objects out of a collection as you put in, but they'd be a lot less obtrusive.
Comments should work now.
"stupid" always raises the level of debate...
Is it possible that understanding of type systems for OO languages has increased over the last decade?
"there's no excuse for the compiler not... to infer the type..."
Look at other languages that target JVM to see what is possible with different type systems:
Nice - http://www.jroller.com/page/dbr
Scala - http://scala.epfl.ch/index.html
Type inference is great, but it's not all roses. If you've made a thinko, sometimes the compiler will infer a type error /over there/ despite your mistake being /over here/ ... and this can lead to lots of headscratching. There's been a few projects which try to solve this issue.
The second, and arguably more pressing problem, is that types are good documentation and if they're not written down explictly in the source code then you'll quickly wish that your IDE could annotate the presentation of the source code with the inferred types in some useful manner. The emacs ocaml mode does this to some degree (middle clicking on an expression tells you its type). Sure, many languages allow you to explicitly state some types and leave others to be inferred, but I find that sometimes expressions which the author thought were "obvious" and left to be inferred weren't so obvious to me, and I'd like them to be explitly typed (or, as I said, have the IDE decorate the presentation of the source code somehow).
That aside, I totally agree with your definition of "stupid" wrt programming languages. Computers are much better at doing boring things than humans. Let the computers do the work so I can go to the pub. :-)
Taking a deep breath and enabling comments again...
Isn't this is another, "Java is too much to type" argument. I remember I thought so 1996 when I came from C and sh and other languages to Java. Over the years I got used to it, mainly cause I feel that typing is not major part of programming. When reading I'm happy about every hint in the code, types are hints. As long as the computers do not do the boring task of reading code I like explicit (and static) types.
BTW there are Java projects (like Thinlet) which heavily use variables of type Object, which still needs typing but at least no thinking any more while writing the code :-)
It's interesting how half the people who replied didn't actually understand the original post. I would have thought it would be more like 80%.
I agree with Charles. Andrew's second point is an interesting one, and I originally agreed with it. Programs should be readable, and type annotations can improve readability. I think there's no question that Tiger's type annotations are incredibly heavyweight, though, and they clutter your program and obscure its meaning after you've added them. The compiler doesn't even try to help you. Take this statement, for instance:
List mylist = new ArrayList();
Why is the second annotation necessary? It shouldn't be: looking back to where the identifier is first declared ought to be enough for the programmer wondering what the container holds. The second tag is just useless clutter. Here it's fine, but in large programs I find it gets in the way.
To Andrew's second point, I think any decent IDE should be able to do the same type inference that the compiler does, and all decent developers should be using decent IDEs. So I don't think there's a need to enforce a bunch of type annotations.
I'm guessing that Steve forgot to escape the angle brackets in his example which should have read something like this:
List list = new ArrayList();
I agree that this stuff is a waste of programmer time. However, the choice alternatives should be carefully chosen. For example, I think the following would be a bad idea
List list = new ArrayList();
because when you look at 'list', you might think that it's a list of 'java.lang.Object'. It's less confusing to leave out the annotation on the second half:
List list = new ArrayList();
Andrew:
Type inference can become messy but that's only if you try to do it all over the place. The simple stuff is guaranteed to be inferable:
var x = y.z.DoSomething();
Since methods can't be overloaded based on return type, this kind of inference is easy. I think it also happens to make your code more agile because now, when you change the return type of 'DoSomething()', you don't have to mechanically fix a whole bunch of compiler errors. Of course, you should still be allowed to use type annotations because there are instances where you'd want the compiler to flag errors, but most of the time I don't want to be bothered.
Frank:
Sometimes the extra typing is just annoying. Often, a meaningful variable name is enough. Also, if you have to use type annotations all over the place, you'll probably start using abbreviated/less-meaningful type names or start using really long expressions to avoid declaring new variables.
I agree, though, that some type of "mouse-hover" type inference in an IDE would be nice.
I have programmed extensively in Java and .Net. I love Java, but elements like type inference and dynamic casting both make .Net a more leveragable language for the kinds of dynamic form generation and my object factories.
Dynamic typing is possible in java with the use of reflection, or even if blocks using the instanceof functions. However, since many people don't have the time to build such a workaround reflection framework (which would noticably slow down the code an increase memory usage) I think that Sun should make a move to allow the jvm and compiler to differenciate types through inferance.
It's probably the only reason that I often use .Net over Java. Why is that again? Because OODB dynaforms are friggen cool.
Oops...my post didn't have the angle brackets either! Silly weblog software interprets the escape sequences when you do a preview.
Evan:
You don't seem to know what you're talking about. .Net is not a language. There's nothing special about the .Net with respect to type inference. There are languages that support type inference and compile to JVM bytecode.