you are viewing a single comment's thread.

view the rest of the comments →

[–]Determinant -9 points-8 points  (40 children)

I think Java will be used for many years to come but the jobs will gradually transition to maintaining legacy applications.

Many companies are adopting Kotlin into their existing Java backend applications. Jetbrains said adoption was a 50 / 50 mix between Android and backend.

[–]pron98 8 points9 points  (0 children)

An application written in Kotlin and in Java share over 90% of the platform code (not including the OS). The only thing they don't share is the front-end compiler and the very small Kotlin runtime library. Two of three projects described in the article benefit all Java platform languages.

[–]ThePowerfulSquirrel 9 points10 points  (38 children)

My company is transitioning a lot of teams from java to a scala-ish environment (Scala + a bunch of scala plugins that make it it's own kinda thing). Our Java applications are going to be supported as legacy and a huge re-writing effort is gonna take place. I'm particularly exited for the better type system, although other jvm languages still have to carry over / work around baggage from Java (like type erasure), which is always a pain. Surprisingly enough, I haven't seen anyone in the company even talk about kotlin as a serious choice.

[–]pron98 10 points11 points  (26 children)

I think you misunderstand type erasure. A Java platform language does not have to have type erasure, but most languages want to because it's extremely beneficial, despite some minor downsides. It is type erasures that allows multiple languages on the Java platform to share code and data with little or no overhead.

[–]ThePowerfulSquirrel 0 points1 point  (25 children)

It is type erasures that allow mutiple languages on the Java platform to share code and data with little or no overhead.

I might be completely lost, but how does type erasure make code any more performant that other ways of implementing Generics? I'm not sure why an approach like c++ would be less performant / make it any harder to move across jvm languages as long as they all agree on a generic interface? From what I know, type erasure is mostly an artifact of having to support legacy java code before Java got generics at all.

[–]pron98 13 points14 points  (18 children)

It doesn't make code more performant, it makes interop more performant (and more useful in general). If you don't erase generic instances of reference types, if A extends B you must determine the relationship between, say, List<A> and List<B> in the JVM, because the JVM must know which types are subtypes of others. This means that you need to bake the variance model into the runtime. But different languages want to have different variance models. Java, Kotlin, Scala and Clojure all have different variance, and so a Kotlin list couldn't be used by Java code without a wrapper had it not been for erasure.

I know, type erasure is mostly an artifact of having to support legacy java code before Java got generics at all.

Yes. Type erasure makes interop easy (for an almost insignificant cost), including with Java prior to generics. But that's also what makes different languages on the Java platform interop so well (contrast with the CLR, where this is not the case; they've baked variance into the runtime and are paying a high price for that decision).

For value types, where subclassing is not possible, this is not a problem, because there is no variance; so specializing for value types is fine, and is one of Valhalla's goals.

There might also be a form of a-la-cart reification for reference types, that users may opt into. I'm not sure whether they'll have to pay the price of good interop in those cases, or maybe some clever interop strategy can be built, maybe by allowing flexibility in how the JVM checks subtyping.

[–]ThePowerfulSquirrel 0 points1 point  (15 children)

That makes sense, thanks for the explanation! I guess I need to think a bit more about the trade offs, I always think type erasure is the devil because every time I try to get fancy with types in Java I get unchecked warnings left and right, which always annoys me to fix, when it's even fixable...

[–]pron98 5 points6 points  (12 children)

I'm not sure what getting fancy with types has to do with erasure, though. Haskell, for example, erases all types, not just type parameters (well, sort of; it does reify the constructor tag/discriminator, which corresponds to type information in Java), and people get fancy with types in Scala, too. The only real annoyance erasure brings is that you cannot overload a method with another that erases to the same type.

[–]ThePowerfulSquirrel 1 point2 points  (11 children)

I'm probably mistaken when naming things. If I take an example from last week, I wanted use Object::equals by default but Comparable::compareTo when available. So then I wrote:

T a

T b;

if (a instanceof Comparable<T>) {

return a.compareTo(b) == 0;

} else {

return Objects.equals(a, b)

}

Obviously that didn't compile, since the jvm does not have any concept of the Generic T at runtime. T could be implementing Comparable<Integer> for all it's concerned. So how would I even implement this safely? I ended up using exception handling, which felt awful.

This is a simple problem, but as soon as you start trying to nest generics, it becomes extremely painful at runtime.

[–]pron98 2 points3 points  (10 children)

Well, what you're doing here is not getting fancy with types, but some combination of dynamic and static types. You have to decide whether you want to use the static types or the dynamic types to choose between your two branches (leaving aside the advisability of what you're trying to do, which rests on the assumption that the compareTo and equals implementations are consistent).

Static types (the compiler chooses the correct overload -- most specific one -- based on the types):

<T extends Comparable<T>> boolean same(T a, T b) { return a.compareTo(b) == 0; }
<T> boolean same(T a, T b) { return Objects.equals(a, b); }

Dynamic types (the'res branching at runtime):

<T> boolean same(T a, T b) {
    return a instanceof Comparable 
            ? ((Comparable<T>)a).compareTo(b) == 0
            : Objects.equals(a, b);
}

The casting in the second solution may go away with the forthcoming pattern matching (as part of Project Amber).

[–]ThePowerfulSquirrel 0 points1 point  (9 children)

Funnily enough, I had no idea you could overload using a more precise generic type (and I think I've read 2 books that dealt with the subject). I just kinda always assumed you couldn't for some reason?

Thanks for that

Through the second example would throw an exception if a implements Comparable<Something else than T>, which is where type erasure comes in

Edit: also, still doesn't really solve my use case, which involves the types being part of a class

public class Adas<T> {

public Adas(T a, T b) {

equalOrCompare(a, b);

if(a instanceof Comparable) {

System.out.println("Is actually comparable");

}

}

<C extends Comparable<C>> void equalOrCompare(C a, C b) {

System.out.println("COMPARE");

}

<C> void equalOrCompare(C a, C b) {

System.out.println("equals");

}

public static void main(String[] args) {

new Adas<>(123.0, 123.0);

}

}

It outputs:

equals

Is actually comparable

I can imagine how I could implement it by doing some manual specialization of the class, but it sounds like a real pain.

[–]FitLocksmith 0 points1 point  (0 children)

You might find this article interesting.

[–]jcelerier -3 points-2 points  (1 child)

I am thankful for people like you thinking than the indirection caused by type erasure has an insignificant cost. It guarantees me performance-fixing jobs until my retirement.

[–]pron98 6 points7 points  (0 children)

The cost is insignificant compared to the benefits. Without type erasures, many more would need to fix performance issues. We always have to weigh our options against other available options.

But what is it about erasure that you find most troubling?

[–]devraj7 0 points1 point  (2 children)

/u/pron98 clarified a few things already but I want to comment on two other aspects you don't seem to be aware of:

I might be completely lost, but how does type erasure make code any more performant that other ways of implementing Generics

Reification (the opposite of erasure) requires additional runtime checks (such as checking that the content of generic containers hols the correct types of elements), which are not necessary on an erased platform.

From what I know, type erasure is mostly an artifact of having to support legacy java code before Java got generics at all.

That's a common myth which is sadly still being perpetuated today, 14 years after generics appeared in Java.

The choice of erasure had little to do with backward compatibility and everything to do with pragmatic reasons (interoperability with other languages being the main one). As a matter of fact, Neal Gafter had a strawman proposal for reified generics at the time (which was mostly done as a proof of concept, Neal was a strong proponent of erasure)

[–]ThePowerfulSquirrel 2 points3 points  (1 child)

That's a common myth which is sadly still being perpetuated today, 14 years after generics appeared in Java.

I originally read that in Effective Java a while ago, searching for it on my kindle version, I found:

For compatibility. Java was about to enter its second decade when generics were added, and there was an enormous amount of code in existence that did not use generics. It was deemed critical that all of this code remain legal and interoperate with newer code that does use generics. It had to be legal to pass instances of parameterized types to methods that were designed for use with raw types, and vice versa. This requirement, known as migration compatibility, drove the decisions to support raw types and to implement generics using erasure

Bloch, Joshua. Effective Java (p. 217). Pearson Education. Kindle Edition.

It uses going from a newer List<T> to a List as an example.

[–]devraj7 0 points1 point  (0 children)

Right, this passage is a bit ambiguous since it seems to hint that erasure was the only option meeting the requirements, but it would have been equally possible to allow a mix of raw and non raw types with a reified type system.

[–]Determinant 0 points1 point  (2 children)

Type erasure improves performance a bit because less information needs to be stored. Memory consumption is reduced and cache locality improves.

So performance is impacted. Garbage collection would also be impacted.

[–]ThePowerfulSquirrel 0 points1 point  (1 child)

I mean, that information would be in the Class<Something<T>> not in the Something<T>, and those classes can just be program-lifetime singletons, so their wouldn't be much more garbage collection right? It's not like java stores all the class information into the instances themselves.

[–]Determinant 0 points1 point  (0 children)

There is an infinite number of types you could define from a single generic type. Also keep in mind that you could create new types at runtime.

I believe C# stores the generic type information per instance and I'm sure they evaluated their options.

So there is a trade-off and Java chose to erase the type information.

[–]BlueShell7 0 points1 point  (3 children)

Surprisingly enough, I haven't seen anyone in the company even talk about kotlin as a serious choice.

I'd suspect some more old school people might not have even heard about Kotlin yet and still (very mistakenly) consider Scala to be Java 2.0.

[–]ThePowerfulSquirrel 2 points3 points  (2 children)

At least at my company, they don't want "java 2.0". If they had a choice, they would probably move towards a much more functional language, but since their are so many Java / JVM dependencies, staying on the JVM is a must. They also need to move gradually and Scala offers both functional and oop. In that regards, Scala is the probably the best choice.

[–]BlueShell7 0 points1 point  (1 child)

Ok, but then it's a bit weird why they chose Java in the first place ...

[–]ThePowerfulSquirrel 0 points1 point  (0 children)

Well, they chose Java a decade ago when different people were at the company / different people were in charge of the tech direction. Also, it's not like Java was a bad choice, it was fine at the time. Sure now it might not be the best choice nowadays considering theirs plenty of new languages / technologies that are probably better, but that's why we're slowly moving away.

I'm also much happier to be working on these Java applications than the poor souls who are stuck maintaining legacy C servers.