1, lack of closure: I don't think this needs to be explained. Functional programming has existed for decades, but in recent years, they have gained more and more attention. The main reason is that it can naturally write parallel programs. I partially agree with Joshua Bloch's emphasis that the problem of introducing closures into Java needs to be reconsidered (the way proposed by BGGA is really bad). At least the lack of closures makes it impossible to do any real functional programming in Java.
2. Lack of first-level function: This problem is related to the previous one, but I think it is more serious. In Java, the only way to achieve a similar effect is to use the famous, ugly and tragic single method to anonymize the inner class, but this seems to be a poor method. Even in C#, it provides a better implementation through the proxy mechanism.
3. primitive types: how perfect it would be if everything was an object in Java, but they just didn't design it that way. So this leads to some problems, such as you can't put an int into a collection, which is solved by the automatic boxing feature in Java5 (mentioned below). This also causes the trouble of passing values and references. Native type data is passed to the method by value (copy a copy and then pass it to the function), while the real object is passed, so it should also be passed by value, but the effect is similar to passing a reference, because the object can be accessed by this object address in the function.
4. Automatic boxing and automatic unboxing: This feature was introduced in Java5 to solve the problems caused by the existence of local types. It allows you to silently convert native types to corresponding objects, but this usually leads to other problems. For example, Integer can be null and int can't, so JVM can only throw a NullPointerException that is difficult to debug. In addition, it may lead to other strange behaviors, such as the following example, which makes it difficult for us to understand why the variable test is false:
Intger a = new integer (1024);
Intger b = new integer (1024);
Boolean test = A<B || A = = B || A>B;
5. Lack of classification of examples: Examples are a cool feature of Java5, but in order to maintain compatibility with the old version of Java, some important features are missing, especially the types of examples cannot be reflected at runtime. For example, you have a method that accepts list parameters, but if you pass in a list, you won't know the exact type of paradigm in the operation. Similarly, you can't create a canonical array. This means that although the following code looks natural, it cannot be compiled:
List[] listsOfStrings = new list [3];
6. Inevitable paradigm warning: Do you find yourself caught in an unavoidable paradigm warning? If you use examples as often as I do, I bet you do. In fact, it is the scale symptom of this problem that makes them think it is necessary to introduce a specific comment (@SuppressWarnings("unchecked ")) to deal with this situation. I think the example should be designed better.
7. You can't pass void to the method call: I have to admit that this need to pass void to the method is a bit weird at first glance. I like DSL. When I implement a specific feature of my own DSL library (lambdaj), I must declare a method as such a signature: Void do something (object parameter), in which the parameter passed in for this method is the result of another method call, and its sole purpose is to register the called object itself for later execution. To my surprise, even if the println method returns void, there seems to be no good reason not to allow me to write code like this:
do something(system . out . println(" test "));
8. There is no native proxy mechanism: proxy is a very effective and widely used mode, but the proxy mechanism provided by Java is only for interfaces, not specific classes. This is why libraries that provide this mechanism, such as cblib, are adopted by so many mainstream frameworks, such as Spring and Hibernate. In addition, because cglib is implemented by creating subclasses of delegate classes at runtime, these methods have a well-known limitation-they cannot delegate final classes, such as String.
9. Bad switch ... case statement: Java specifies switch ... Case can only choose int and enum (starting from Java 5). Compared with more modern languages such as Scala, this seems too weak.
10, Checked exception: Similar to native types, Checked Exception has also become the source of evil in Java. It forces programmers to do one of the following two very bad and annoying things: drown your code with a lot of attempts ... catch bad, difficult-to-read and error-prone statements, and the greatest significance of this is just to package the captured exceptions into runtime exceptions and then throw them; Or let a large number of thrown declaration clauses pollute your API, making the interface inflexible and extensible.
The real problem is that the only solution to the main problems I mentioned here is to make a painful choice, define a new set of language specifications and let go of the backward compatibility of the current version. I guess they will never do this, although I believe that it is not difficult to write a program that can automatically convert the old Java source code and make them compatible with the assumed new version. Finally, this is why I decided to start looking for a better JVM compatible language.