There are trade offs to be had in every design choice.
When you have a "pure" OO language (see if you can get anyone to agree on what that is), when you do addition, rather than having the system get two numbers and add them, you are instead first invoking the add
method on the object and getting back a new one (making mutable Number Objects can make for difficult times).
An example of this would be the BigInteger class. Instead of int fourtyTwo = 6 * 7;
you've got BigInteger fourtyTwo = new BigInteger(6).multiply(new BigInteger(7));
You've created a total of three objects and done some math in the background. This is also a little bit of a mess, but thats just the semantics - you can hide that with operators, but its still doing all that work behind it.
From C2 Is Java Object Oriented the reason becomes clear:
Lastly, it is a hybrid to provide performance gains. Even though Smalltalk has an advanced virtual machine, its inability (when I was using it) to provide a machine-correlated bytecode for integer math placed a significant performance impact on its entire environment. Being a hybrid, Java cannot be called a true Object-Oriented language. But then, why does it matter? ;-) Use the right tool for the job and life will be happy!
You can see what is being mentioned there in some docs for SmallTalk:
Thus, math is not a special case in Smalltalk; it is done, exactly like everything else, by creating objects, and sending them messages. This may seem odd to the Smalltalk novice, but this regularity turns out to be quite a boon: once you've mastered just a few paradigms, all of the language “falls into place”. Before you go on to the next chapter, make sure you try math involving * (multiplication), - (subtraction), and / (division) also. These examples should get you started:
While yes, it does men that some things can be more elegant in the language design it also means that its going to be slower. From a talk on SmallTalk and Objective C:
What makes things slow?
- Small integer arithmetic
- Boxing
- Dynamic message lookup
- Memory management operations
Another example of this performance can be seen at the Language Benchmarks Game for Ruby vs Java - where Ruby implements the "pure" OO style math - messages are sent to the object to add rather than working with primitives. Note that in many cases Ruby is one or two orders of magnitude slower than Java. These benchmarks are all math heavy, and when looking at the Java code, they're all using primitives.
There are things you can do to speed that up, but its still less than optimal. Early Java was plagued by performance concerns. Making math that much slower would have been awful.
Lets look at a high performance Java architecture: LMAX. Every iota of performance was pulled out of this, in some cases rewriting some rather basic things.
It took a bit more cleverness to go up another order of magnitude. There are several things that the LMAX team found helpful to get there. One was to write custom implementations of the java collections that were designed to be cache-friendly and careful with garbage. An example of this is using primitive java longs as hashmap keys with a specially written array backed Map implementation (LongToObjectHashMap). In general they've found that choice of data structures often makes a big difference, Most programmers just grab whatever List they used last time rather than thinking which implementation is the right one for this context.
Writing a HashMap that could work on <long, Object>
rather than <Long, Object>
allowed for some significant performance gains by not having to do object based equality for two objects (could just use ==
) and avoiding a number of issues with garbage collection (look at your heap some time and count how many Long
objects are sitting around when you're heavily using HashMap
s).
Lastly consider that Java's hotspot optimizer will compile code to native when it needs to. The code produce by int fourtyTwo = 6 * 7
is much simpler and faster than the corresponding BigInteger fourtyTwo = new BigInteger(6).multiply(new BigInteger(7));
Its all about trade offs, and this was one where performance won out over "pure" elegance.