From the very beginning, Java designers consciously made decisions to attract the C++ community, and favor performance over other considerations. The biggest compromise was the inclusion of primitive types. This addition means Java is not fully object-oriented, and presents several significant challenges. Those who came from the C++ community don't always see a problem, but developers from other programming languages often see primitives as an ugly kludge. Primitive types do not descend from Object, so Java is more of a hybrid language than a true object-oriented language. But that's all academic. There's a real cost associated with the theory.

Primitives Are Limited

Java primitives limit you because they don't descend from a common Java object. One of the nice things about most object-oriented languages is polymorphism: you can deal with specific objects in a general way. In Java, that's not quite true, because primitives do not descend from Object. You can't, for example, say 6.clone( ), or 6.getClass( ).

If you've ever built an XML emitter or an object relational mapper, you know about the headaches related to primitive support. In Java, you can't treat all types the same, and you don't have the benefit of natural methods on the primitive types. You have to build in explicit support for objects, primitives, and arrays.

Since most of us don't build XML emitters or persistence frameworks, we shouldn't care about those costs, right? It's not that easy. You still have to deal with complications in the language, such as inconsistent APIs and added breadth of the frameworks that you do support. Reflection is probably the worst. To get the value of a field, you first have to determine the type. You then get the value, with one of get, getBoolean, getByte, getChar, getdouble, getFloat, getInt, getLong, or getShort. Of course, if it's an array, all bets are off. Arrays can contain primitives or objects, so they can't even treat their contents generically. You basically have to go through the whole process again.

Reflection in pure object-oriented languages is much simpler. To get a field's value, you use a single API to query a field, and get an object back. You can then query the object to find the defining class. If you want to deal with it as a top-level object, you don't even have to do that.

Primitives Are Unnaturally Verbose

Of course, you need to be able to do some things to a primitive that the primitive itself can't do. Java solves this problem by providing type wrappers. Primitives are so awkward because sometimes you use the primitive and sometimes you use the wrapper. It's very difficult to be consistent with usage.

When you add the additional wrappers and casts, you find that primitives don't help make Java cleaner, and they make it only marginally faster. Since you have both types and wrappers, you often need to convert between the two, forcing unnecessary syntax, and often unpredictable behaviors (such as several strange behaviors in the autoboxing in Java 1.5).

The Big Trade-off

All in all, primitives were important in one sense: supporting them let Java aggressively attract C++ developers, because the idea and syntax were similar. In retrospect, though, it's created some significant problems, in terms of language clarity, productivity, and readability.

In retrospect, we're paying for the early compromises that it took to draw away the C++ community. The next popular programming language will probably not be a hybrid language, with both objects and primitives. C++ started the transition to object-oriented programming and Java finished it. We don't need a crutch anymore.