I read a lot of blogs and see people all the time talking about bad things in the java programming language; a lot of them are about annotations and generics that were added to the language in 1.5 release. What are the things in the language or the API that you don't like or would design differently?
1.upto(10)
for example. With auto-boxing, the worst things about using primitives have been fixed. - KitsuneYMG
I really don't like the manipulation of dates in Java (java.util.Date, java.sql.Timestamp, java.sql.Date, java.util.Calendar).
generics-as-afterthought
Arrays are covariant which shouldn't be.
See: Wikipedia Article [1]
[1] http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)I think the Java language is overall well designed. I just think it's way too verbose.
(!R)@&{&/x!/:2_!x}'!R
gives you all the prime numbers between 1 and R. How? Don't ask me. ;) - Peter Lawrey
Ever since I have learned python, I just find that Java lacks the possibility of returning multiple values easily:
#In python:
name, age = john.summary()
//In Java:
Object[] summary = john.summary();
String name = (String) summary[0];
int age = ((Integer) summary[1]).intValue(); #primitive types require boxing
Edit: as mmyers pointed out, the following is legal as of Java 5:
int age = (Integer) summary[1];
On the java platform: AWT, and the XML APIs (it's amazing what you have to do just to parse a String into a DOM tree)
On the language: add type inference and tuples, generics without erasure, make 'volatile', 'strictfp' and 'transient' annotations
native
an annotation too. I also prefer JNA's access of native functions to JNI - KitsuneYMG
Primitive types (ints etc.) are not objects like in Smalltalk, forcing the programmer to use boxing classes such as Integer.
Smalltalk integers are real objects. In order to avoid overhead, they are not implemented as "boxed" primitives, like Java does, but instead as "immediate" objects: the object's value is stored within the object "pointer" (the same is true with individual characters) To do so, it uses the pointers' low bits, which are always zero for regular objects (since structures are usually word-aligned), as "tags" to differentiate immediate objects with the former. For example, if a pointer's bit 0 is set, it designates a SmallInteger object: its integer value is stored in the remaining 31 bits. Larger integers use regular objects with infinite precision (AKA bigint).
Java 5's autoboxing is the worst of all solutions, because it hides the overhead behind syntactic sugar: the programmer is unaware of the fact that objects are created behind his back although he's using primitives. Not only that, but several Integer objects could be created for the same int value. Whereas in Smalltalk, the value IS the objects. This also allows the compiler to generate optimal code, in a way that can't be done with boxed objects. Smalltalk proves that primitive types can be objects AND be implemented efficiently.
This is one of the reasons why Java cannot be considered a high level language (not that this can be a problem or drawback in any way, there is ample room for system level programming languages).
All References are nullable
All references are nullable, which causes a lot of NullReferenceExceptions. I think something like "Option" (Scala) is better suited for those rare cases where an object reference actually should be able to be "Nothing". Of course that would have required Generics and some Pattern Matching right from Version 1.
null
but can be is enums. You can have a switch(myEnum) { default: }
but have it fail with an NPE. - Peter Lawrey
The test-unfriendly servlet API... you need a framework to be able to mock a bloody Request!
No destructors, therefore no possibility of RAII [1].
Most objects are memory-only, which Java's GC handles just fine. But whenever you have a class that allocates non-memory resources (e.g. DB handles), you need to remember to clean it up in a finally
block every time you create an instance -- so you need finally
blocks everywhere containing the same cleanup code, and if you forget one place, bang, resource leak as soon as an exception is thrown in that scope.
This is one of the (few?) things C++ got right -- you write the cleanup code just once in a destructor, and the language guarantees that it will always be called when an instance of that class goes out of scope, even in the event of an exception. I realise that Java is GC-collected, but RAII and GC are not mutually exclusive -- there just needs to be a way to specify deterministic destruction at scope exit for a particular class or instance, and to provide a destructor. Really, you would not believe how much this simplifies resource management.
It's been 5 years since I touched Java, maybe this has changed?
[1] http://en.wikipedia.org/wiki/Resource_acquisition_is_initializationKeeping too much C syntax that was known to be problematic. The switch statement should have been redone for Java. Operator precedence is at least a more complicated problem; while Java could do better than C there was some advantage in keeping it.
One goal for C++ was to be at least a better C, and so Stroustrup had an excuse for keeping bad C decisions. Gosling et al. didn't.
Edit: Also octal literals (thanks, Dan Dyer, for pointing that out). Those are very useful things for writing OS internals and bit-grovelling code, which are applications Java isn't usually used for.
goto case B;
" - finnw
private
and final
for fields by default. - Peter Lawrey
Checked exceptions are a problem in Java because they may break encapsulation.
There is an interview [1] worth reading with Anders Hejlsberg on Artima where he talks about that:
[1] http://www.artima.com/intv/handcuffs.htmlAnders Hejlsberg: Let's start with versioning, because the issues are pretty easy to see there. Let's say I create a method foo that declares it throws exceptions A, B, and C. In version two of foo, I want to add a bunch of features, and now foo might throw exception D. It is a breaking change for me to add D to the throws clause of that method, because existing caller of that method will almost certainly not handle that exception.
try
block or method, such that if method Foo
specifies dontcatch BlahException
, then any BlahException thrown from any code called from Foo
would be rethrown, wrapped in a RuntimeException
. Note that if Foo
also declared throws BlahException
, then any blah exception thrown within the code for Foo
would propagate up as a BlahException
, but those from methods Foo
calls would become RuntimeException
. - supercat
Too much focus on simplicity at the expense of expressiveness. That basically sums it up.
No destructor. I wish there was a standard (optional) way to say "I'm done with this, run some code", rather than relying on the finalize method that may never get called. I'm not talking about having to manage memory, just that when you do want to finish with something, there was a standard call to make which would do whatever clean up you wanted and then provide (maybe) some hint to the Garbage Collector that you're done with it. I'm sick of having some classes with a "close" method, and some with a "done" method, and so on and so forth.
IDisposable
interface and implement it on the relevant objects. Even without syntactic sugar like a using
statement (C#), everyone will know what it means when you implement that interface and you won't me fighting with what to call the Dispose
method (dispose()
in Java?). - 280Z28
Closeable
to indicate automatic resource management - KitsuneYMG
Closeable
and many resources have had a close()
method from the start. What Java lacks is direct support for it, but there is nothing stopping you add a method to do this. - Peter Lawrey
Not necessarily things they did wrong, but things that I would have liked:
operator+=(...)
differ from allowing me to do whatever I want with 'append(...)'? Both are arbitrary names for an action. Just because I think I know what append
does doesn't mean I should skip reading the javadoc for it. The same can be said of operator+=
- KitsuneYMG
virtual
operators. Or override non-virtual functions. They deserve death. Possible by shoving iPads into uncomfortable places until they pop. - KitsuneYMG
+
works for Strings. - Peter Lawrey
JNI could be one of the worst in Java.
It is much much more time wasting if you compare it with PInvoke in .NET.
new Integer(0).getClass()
in code returns a Class<?> not Class<Integer> however when you compile the code it returns Class<? extends Integer>, thank you @Mr. Shiny and NewField
and Method
should have been supported in the language rather than via a library. - Peter Lawrey
Ugly looking default GUI, I think Java would gain a lot if they provided a nice default look-and-feel.
Java believes its own hype.
No way to take off the training wheels. Whenever I have to use Java I feel like I am on a kiddy trike.
It is overly difficult to reuse code. Other languages encourage decoupling shared functionality from a single object, but in java the only way is inheritance which is often blocked for other reasons. How do you add a method to String?
No first class functions. Not everything is always object oriented.
Tying the class name to the file name. This makes it hard to use the same file in multiple projects.
No pointers.
No preprocessor. Unless your projects are very simple or very isolated, having a preprocessor is invaluable.
No manual memory management. It is convenient to ignore memory management sometimes, but not to be forced to it.
No way to tell if an object implements a method. The information is there, but hidden because somebody might be allergic to details or something.
No way to call a method on an arbitrary object by name. Right now you basically have to make an interface for every single function, when that should just be implied.
No consistency in the core data structures. There are so many implementations of lists and maps and vectors and arrays and none are interchangeable.
Not cross platform. The one strength of java should be that once you port the virtual machine everything else just works, but that is not always the case in practice. I have one product for two platforms and very little code works on both. There is not a single file that I could use in both versions of the same program without some modification.
Java always feels so slow. You can argue that it is just as fast as this or that, but my perception is always that if it was not java it would be much faster.
A couple of years ago I appreciated Java more than I do today.
I fell deeply in love with their packaging. Today I despise it. A gazillion classes all over the place packaged in a way that would have very little sense hadn't you had experience armed with you. Check out the language ref for AS3 [1] and see what I mean. The 2nd day I started working in AS3 I wasn't going on google looking for tutorials on how to do something, I was already a natural, knowing instantly where that class that I never knew even existed, that did exactly what I needed, was located.
Java still has a great community, but it's not as intuitive getting involved in their communities as it is with other languages. Other languages have provided way better developer portals, way better and more resources.
Basically, Java got too big to handle, got big before they'd lain out everything for its growth. It's too bogged, too confusing. They used to be the forward thinkers in so many aspects, now they are good in few and in some we just wish Java would collapse once and for all.
[1] http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/In JDBC the java.sql.Date class has no time component. This forces you to use Timestamp, which confuses Oracle when your dates are stored as Oracle DATE values.
Also I'm not crazy about how Java is packaged. It's annoying to have to set up class paths and jar files and don't get me started with EAR files and WAR files. There is room for improvement here.
I tend to get a little irked by the deprecation of good useful objects and their replacement with ugly, hard to use objects (example: Date & Calendar).
GC
While GC is incredibly convenient, an unpredictable GC is not suitable for certain applications. One such example would be hard real time systems. In a hard real time system a single unexpected delay can result in mission failure.
Examples of such hard real time system would be: fly by wire systems on a fighter jet, navigation systems on a missile, robotic arms that perform surgery, etc.
Also, getting killed in a real time video game is also a mission failure. You don't want to GC right at the moment the player pressed the "dodge the giant fireball that will kill my character and force me to redo the 20 minute long level," button. That is a very important button. (Although it has been noted that in multi-tasking OS you cannot control the task priority which could easily be worse than a large GC operation.)
Static Polymophism
Ignoring static polymorphism. Static polymorphism could go beyond "C++ like" templates, and it is a very useful optimization. Generics as implemented in Java is still dynamic and it loses that opportunity to eliminate run-time type checking where compile time type checking would is enough. Of course is possible for increased code size to reduce performance more so than dynamic types would. As with all optimizations it should be profiled.
Everything is in a class
Although nit picky, there are times when you just want a function. Currently in Java you must put such functions in a class as a static function. A better solution would be a function in a namespace. While a class can work like a namespace, classes cannot span multiple files and libraries.
I like Java a lot, but mainly two things bug me:
Also I think doSomething()
looks worse than DoSomething()
, but that is merely a coding standard, and not really a problem.
this
pointer onto the stack, and class-instances need to dereference to get the value. If anything, having stack-based instances would increase the performance much more than structs, but even that would be such a small increase it just wouldn't be worth it for the extra syntax/beginner-confusion. - BlueRaja - Danny Pflughoeft
Making up a whole new logging api (java.util.logging) to be the new standard was a mistake that annoys me. I have to create a subclass of a Formatter just to get output on only one line! What was wrong with log4j?
Not that important, but something I never thought about before today:
Object
class
should be abstract
[1].There are serious downsides which result in boilerplate patterns:
...and so on.
ObservableCollection<T>
, but it's not a C# language feature and doesn't get any syntactic sugar. Is there some aspect of them that you'd like to see in the Java language as opposed to simply implementing them in a (possibly standard) class library? - 280Z28
java.nio.Buffer, ByteBuffer, etc. are classes, not interfaces, and have no way of allowing you to wrap anything but arrays of primitive types (byte[]
for ByteBuffer) in a Buffer so you can pass the resulting object to a method which uses a Buffer.
No public mutable BigInteger and BigDecimal for more performant compound operations and the way requests for them gets always turned down citing the evilness of mutable objects in multi-threaded application. Please then remove StringBuilder as well!
Using question marks instead of named placeholders in PreparedStatements - who wants to count each question mark all the time to check if everything got assigned?
Inconsistent usage checked/unchecked exceptions in the runtime: Integer.parseInt(String) unchecked, String.getBytes(String) checked.
Sub package cross-dependencies in the runtime: java.lang <-> java.io?
Failure by Sun to incorporate new APIs into the platform quickly. If you're new to Java, and want to do some logging, there are about 100 different ways to do it. Which should you use? Who knows. There's too many choices, and you have to find out which one fits your needs. There should just be one way to do it, or at least a recommended way to do it.
Ones that have already been mentioned:
The one that hasn't been mentioned yet is the lack of basic enums. Sure, they added something they /called/ an enum in 1.5, but it's not. It's a neat construct, and it has its uses, but it's not an enum and it doesn't cleanly replace just having a bunch of constant int values.
There are other things that bug me, especially the things that make Java so slow, but those five are the things that irk me most from a strictly code development viewpoint.
Java mixes up value and object identity equality by using the same operator ==
for both. Hence you have to use ==
for int
, but equals()
for Integer
, and so on. A good example of a language doing this right is Python (value equality: ==
and !=
, identity comparison: is
and is not
) and VB (value equality: =
and <>
, identity comparison: Is
and IsNot
).
[Hmmm... not sure why the downvotes...]
Java should not have used the same syntax as C++ with different semantics.
A couple of examples
meaning of "protected" is different
meaning of "Dog d" is different
This causes a great deal of confusion with C++ folks who learn Java. I still see many people shocked when they hear that "protected" includes other elements in the same package...
(If you don't think Java has pointers, see http://javadude.com/articles/passbyvalue.htm)
I'm by no means an authority for what Java is doing wrong, but from this rant on comp.lang.lisp [1] it looks like there is logic error in their exception handling. Is that a fair assertion?
[1] http://groups.google.com/group/comp.lang.lisp/msg/6157c055503e8ba8I don't like how collections in Java have a toArray() method that returns an Object[]. It's very difficult to cast between a primitive array and a collection.. You end up doing this:
ArrayList<Integer> list = callSomeWeirdAPIMethod();
Object[] f*ckedArray = list.toArray();
int[] realArray = new int[list.size()];
//...copy elements from f*ckedArray to realArray
where you can't do this:
int[] realArray = (int[]) list.toArray();
or even this:
Integer[] realArray = (Integer[]) list.toArray();
int[] realArray = Ints.toArray(list);
- finnw
One thing that bites me regularly is the fallthrough behaviour of the switch statement.
Reading the replies made so far, I notice the almost complete absence of multiple inheritance. It is mentioned only once, but used to be a subject of heated discussion a number of years ago. I'm wondering why this is the case. Do people feel it is not really needed? Do people no longer care?
Lack of Class modifier Friend. One long and verbose class can be broken into two Friend-ly Classes in C++, but not in Java. A static inner class will do the same thing as long as the code never needs to be reused.
friend
, though. - Pavel Minaev
Non-resizeable arrays. Yes I know you can allocate another array and System.arraycopy() the original... but would it have been so hard to have, e.g., array.resizeTo(x), array.growBy() and array.shrinkBy().
And no way to make an array entirely final (it's reference and all of it's elements).
Edit: Arrays don't implement the Collection or List interfaces.
The collection hierarchy in the standard library is completely broken as designed. Just as an example, the List
interface has 25 methods that you have to implement, and most of them mutate the list. Collections rely on the elements implementing equals
and hashCode
properly, which must be done by inheritance, which disfavours composition as a design strategy if you want your libraries to work with the standard library.
In two words: Turing Tarpit.
Bundling .jar files: 1st: .jar files don't really have icons (wtf?!), then you cannot include .jars in .jars (omgwtffff). Together: write your code once, run it anywhere and write a 6-page manual for every platform on how to properly bring your UI up. Include sentences like: "we recommend setting the classpath. to do that, run msconfig in a shell"
Make a contract with the nearest suicide aids provider.