A couple of points. First, the argument you're making is for static -- i.e. compile-time -- semantic checking. I was hearing the very same argument thirty years ago among people who advocated Pascal over C; on paper it's sound, but decades of practice have convinced me that static checking, while probably helpful, is not as efficacious as it seems like it should be. After all, you *still* run into mishandled exceptions in Java, and many Java programmers do an end-run around around much of the compile-time restrictions by using runtime exceptions; in fact wrapping low-level checked exceptions in runtime exceptions are a feature of some frameworks. It's hard to get programmers out of the mindset that a core dump, or an unhandled exception, is some kind of serious calamity. You don't want them in production code of course, but they're far less calamitous than continuing processing with bad data. The place to catch those problems is in testing.
As for the other kind of compile-time problem, handing an object to a routine that expects an interface the object does not support, what scripting languages like Python have shown is that it's not that big of a problem. Large, sophisticated systems programs have been written in Python, which lacks precisely this kind of checking.
I'm very comfortable with Java, but there is something about the design of the language and its core libraries which encourages over-engineering. For example, itis possible to do asynchronous http handling in Java EE, like you do in Node.js, but in Node.js you simply create a non-blocking event handler for every case you want to handle. In Java EE, if memory serves, you create a ServletContextListener with a Queue member, and put that member in the servlet context. Then in your servlet you create an AsyncContext and put it in the Queue. None of this is as complicated as it sounds, but it only covers the case when you want to keep a socket open to the client, like when you're doing Comet. If you want to conserve resources by serving your I/O requests using polling rather than blocking threads (the primary Node.js use case), you've got to use java.nio, which can get complicated.
On paper, designing programs in Java is a cinch with Java's rich OO features. Any reasonably competent programmer can quickly learn enough to demonstrate facility with the *features* of the language. In practice as a programs required feature set grows, the number of classes and interfaces explodes, unless you are a very talented software designer.