Reputation: 2611
Currently, I am learning Scala and I noticed that Type Hierarchy in Scala is much more consistent. There is Any type which is really super type of all types, unlike Java Object which is only a super type of all reference types.
Java approach led to introduction of Wrapper Classes for primitives, Auto-boxing. It also led to having types which cannot be e.g. keys in HashMaps. All those things adds to complexity of the language.
Integer i = new Integer(1); // Is it really needed? 1 is already an int.
HashMap<int, String> // Not valid, as int is not an Object sub type.
It seems like a great idea to have all types in one hierarchy. This leads to the question: Why there is no single hierarchy of all types in Java? There is division between primitive types and reference types. Does it have some advantages? Or was it bad design decision?
Upvotes: 0
Views: 302
Reputation: 103903
That's a rather broad question.
Many different avenues exist to explain this. I'll try to name some of them.
Programming languages are in the end defined by their community; a language that nobody but you uses is rather handicapped, as you're forced to write everything yourself. That does mean that the way the community tends to do things does reflect rather strongly on whether a language is 'good' or not. The java community strongly prefers reasonable backwards compatibility (reasonable as in: If there is a really good reason not to be entirely backwards compatible, for example because the feature you're breaking is very rarely used, or almost always used in ways that's buggy anyway, that's okay), the scala community tends to flock from one hip new way of doing things to the other, and any library that isn't under very active development either does not work at all anymore or trying to integrate it into more modern scala libraries is a very frustrating exercise.
Java doesn't work like that. This can be observed, for example, in generics: Generics (the T in List<T>
) weren't in java 1.0; they were introduced in java 1.5, and they were introduced in a way that all existing code would just continue to work fine, all libraries, even without updates, would work allright with newer code, and adapting existing code to use generics did not require picking new libraries or updating much beyond adding the generics to the right places in the source file.
But that came at a cost: erasure. And because the pre-1.5 List
class worked with Objects, generics had to work with Object
as an implicit bound. Java could introduce an Any
type but it would be mostly useless; you couldn't use it in very many places.
Erasure means that, in java, generics are mostly a figment of the compiler's imagination: That's why, given an instance of a list, you cannot ask it what its component type is; it simply does not know. You can write List<String> x = ...; String y = x.get(0);
and that works fine, but that is because the compiler injects an invisible cast for you, and it knows this cast is fine because the generics give the compiler a framework to judge that this cast will never cause a ClassCastException (barring explicit attempts to mess with it, which always comes with a warning from the compiler when you do). But you can't cast an Object to an int, and for good reason.
The Scala community appears to be more accepting of a new code paradigm that just doesn't interact with the old; they'll refactor all their code and leave some older library by the wayside more readily.
Scalac will infer tons of stuff, that's more or less how the language is designed (see: implicit). For some language features you're forced to just straight up make a call: You're trading clarity for verbosity. There where you are forced to choose, java tends to err on the side of clarity. This shows up specifically when we're talking about silent heap and wrapper hoisting: Java prefers not to do it. Yes, there's auto-boxing (which is silent wrapping), but silently treating int
which, if handled properly, is orders of magnitude faster than a wrapped variant, as the wrapped variant for an entire collection just so you can write List<int>
is a bridge too far for java: Presumably it would be too difficult to realize that you're eating all the performance downsides.
That's why java doesn't 'just' go: Eh, whatever, we'll introduce an Any type and tape it all together at runtime by wrapping stuff silently.
In java (and as scala runs on the JVM, scala too), there are really only 9 types: int, long, double, short, float, boolean, char, byte, and reference. As in, when you have an int variable, in memory, it is literally just that value, but if you have a String variable, the string lives in the heap someplace and the value you're passing around everywhere is a pointer to it. Given that you can't directly print the pointer or do arithmetic on it, in java we like to avoid the term and call it a 'reference' instead, but make no mistake: That's just a pointer with another name.
pointers are inherently memory wasting and less performant. There are excellent reasons for this tradeoff, but it is what it is. However, trying to write code that can deal with a direct value just as well as a reference is not easy. Moving this complexity into your face by making it relatively difficult to writing code that is agnostic (which is what the Any
type is trying to accomplish) is one way to make sure the programmers don't ever get confused about it.
Add up the 3 things above and hopefully it is now clear that an Any
type either causes a lot of downsides, or, that it would be mostly useless (you couldn't use it anywhere).
However, there is good news on the horizon. Google for 'Project Valhalla' and 'java value types'. This is a difficult endeavour that will attempt to allow a lot of what an Any type would bring you, including, effectively, primitives in generics. In a way that integrates with existing java code, just like how java's approach to closures meant that java did not need to make scala's infamous Function8<A,B,C,D,E,F,G,H,R>
and friends. Doing it right tends to be harder, so it took quite a while, and project valhalla isn't finished yet. But when it is, you WILL be able to write List<int> list = new ArrayList<int>
, AND use Any
types, and it'll all be as performant as can be, and integrate with existing java code as best as possible. Project Valhalla is not part of JDK14 and probably won't make 15 either.
Upvotes: 2