Live data from Hacker News

A categorized list of all Java and JVM features since JDK 8 to 16

advancedweb.hu

201–210 of 243 posts

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#201

Earlier quoted context omitted.

> Contrary to what you say it is the best language I've ever used for concurrency. It has it all, structured concurrency, cancelation, Flow, transparency (no await), etc. I disagree. Have you ever tried to actually implement something non-trivial that takes advantage of structured concurrency with cancellation? It's pretty hard to do correctly. Can you really tell me off the top of your head what the difference is be…

> I'll admit that I have a glaring experience gap with .NET languages, so I can't honestly say anything about C# and F#. C#/.NET comes with language-level mutexes (`lock`) and the .NET library has thread-safe generic collections (ConcurrentDictionary, ConcurrentBag) and true immutable collections (ImmutableArray, ImmutableList, ImmutableDictionary) with optimized copy operations (e.g. ImmutableList.Add is O(1), but I…

That's good to hear.

Java/Kotlin also have mutexes- just not as language built-ins (well, it does have `synchronized`).

They also have a ConcurrentFoo set of collections as well. And actually an ImmutableMap (but I don't see ImmutableList, etc. Why?).

The "problem" is that they're opt-in.

I spent years writing multi-threaded C++. But I've become very spoiled with modern languages that make concurrency safe(r)-by-default, such as Rust, Clojure, and Elixir (I haven't actually used concurrent Haskell).

Honestly, it's not anywhere near as bad as it used to be. The ecosystem(s) have embraced immutability everywhere that it's possible, so you don't have to worry quite as much about accidents.

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#202

Earlier quoted context omitted.

> Contrary to what you say it is the best language I've ever used for concurrency. It has it all, structured concurrency, cancelation, Flow, transparency (no await), etc. I disagree. Have you ever tried to actually implement something non-trivial that takes advantage of structured concurrency with cancellation? It's pretty hard to do correctly. Can you really tell me off the top of your head what the difference is be…

> Coroutines use unchecked exceptions for control flow That's... horrible. Why can't Kotlin do it the way C# does with heap-allocated state-machines?

Honestly, I truly don't know and I don't feel qualified to judge the merits of the two approaches. There could be performance or behavior implications that they just prioritized differently from how C# does it. Or maybe something to do with the Java underpinnings. Are C# promises/whatever cancellable?

But, as an "end user", the exception thing drives me absolutely nuts. It wouldn't be nearly as bad if Kotlin either didn't use exceptions for ALL error handling, or even if it had checked exceptions so that "normal" errors would be, mentally, separate from fatal errors and/or coroutine cancellation.

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#203

Earlier quoted context omitted.

I don't know about Ada, but I enjoy Rust's strictness when it comes to numeric types. > Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap. EXACTLY. It's f-ing stupid. C's excuse was compilers doing magic on UB or whatever. Java has no such excuse. They just wanted it to behave the same as C/C++ to attr…

> EXACTLY. It's f-ing stupid. C's excuse was compilers doing magic on UB or whatever. Java has no such excuse. They just wanted it to behave the same as C/C++ to attract C++ devs. But... as you yourself are saying, Java's behavior is not "the same as C/C++". Java wraps while in C and C++ signed overflow is undefined. (Interestingly, C++ is now moving away from UB for this, and defining wrapping semantics. While I'm n…

I understand that there are performance implications.

But a 20% to 40% slowdown for number crunching in a language that is primarily designed for writing super indirection-heavy, heap-allocation-heavy, application architectures is just nothing.

Having some kind of high performance math section of the standard library would be fine. But the default behavior is, frankly, dangerous. And for a 20% speed up on operations that are probably far less than 1% of the typical Java application?

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#204

Earlier quoted context omitted.

My opinion is that a high-level language like Java has no business making me guess how many bytes my numeric values will occupy. It's insane. Since when does Java give a crap about memory space? "Allocations are cheap!" they said. "Computers are fast!" they said about indirection costs. Then they stopped and asked me if I want my number to occupy 1, 2, 4 or 8 bytes? Are you kidding me? Yes, you should have those type…

OP mentioned "Ada's approach to types", as well. Ada lets you write stuff like "T is range 1 .. 20" or "T is range -1.0 .. 1.0 digits 18". This then gets mapped to the appropriate hardware integer or floating-point type.

Yeah, I've read little snippets like that from blog posts and stuff, but I've never written a single line of Ada, so I really don't know how that works out in practice.

What happens if you overflow at runtime? A crash, I assume/hope?

My point of view is that this is the opposite of what I'm talking about anyway. Java is a high level language where we are usually writing in Java because we're agreeing to give up a lot of raw performance (heap allocations, tons of pointer chasing) in order to have convenient models (objects) and not have to worry about memory management, etc.

In light of the above, I don't see why the default for Java is to have these really nitty-gritty numeric types. I don't want to guess how big a number can be before launching my cool new product. Just like I don't use raw arrays in Java and have to guess their max size- I just use List and it will grow forever.

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#205
post #199

Earlier quoted context omitted.

Kotlin does indeed (mostly) fix null. Kotlin does not fix the issues with checked exceptions. It gives up and gives us nothing for error handling. So, for all the beauty and magic of a strong static type system, I have absolutely no idea if `fun foo(i: Int): Int` is just going to crash my program when I give it -1. "Do you have a fatal error? Throw an exception." "Do you have a non-fatal error that's totally expected…

Just as an additional detail: Java has a new DateTime and related classes, that I believe solves every(?) problem with the old Date class.

Well, Java 8 isn't really "new" anymore. :)

But, I was complaining about the 8+ DateTime API. Now, granted, it's WAY, WAY, better than the old API. And it's much better than what's available in at least several other languages (cough JavaScript with no time zones at all).

It still depends on a global, mutable, time zone variable. It uses it in several places where it's kind of hard to get it to just use a given time zone.

The API is also just really hard to discover and use correctly. Look at what classes implement TemporalAccessor. It's not easy at all to guess which methods will throw an exception for a given implementer. Further, what if your function is just given a TemporalAccessor? I have NO IDEA what I'm actually able to call on the object without it exploding.

Calendar is awkward and hard to use.

Switching around between TimeZone, ZoneOffset, and ZoneID is pretty cumbersome. Some stuff takes Strings, some take ZoneID, etc. Some things that seem to take ZoneID don't seem to be able to take a custom TimeZone (not that I think I ever really needed that- I was trying to do something hacky IIRC).

A lot of methods take Long, which you have to just read the docs to know if that's seconds or milliseconds.

It's just kind of a bleh API.

Then you mix in the fact that JDBC is forced to use the old Date classes and that messes everything up because of timezone crap.

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#206

Earlier quoted context omitted.

My opinion is that a high-level language like Java has no business making me guess how many bytes my numeric values will occupy. It's insane. Since when does Java give a crap about memory space? "Allocations are cheap!" they said. "Computers are fast!" they said about indirection costs. Then they stopped and asked me if I want my number to occupy 1, 2, 4 or 8 bytes? Are you kidding me? Yes, you should have those type…

> the default should basically be a smart version of BigInteger that maybe the JVM and/or compiler could guesstimate the size of or optimize while running. I suspect this would be disastrous for performance. I believe Haskell uses a similar approach though. Sometimes you want to store 20 million very small values in an array. Forcing use of bigint would preclude doing this efficiently (in the absence of very smart co…

> I suspect this would be disastrous for performance. I believe Haskell uses a similar approach though. > > Sometimes you want to store 20 million very small values in an array. Forcing use of bigint would preclude doing this efficiently (in the absence of very smart compiler optimisations that is).

I suspect that it would. I also suspect that I don't care. :p

We're talking about Java. Yes, you can write high-performance Java and I wouldn't want to take that option away. But look at the "default" Java application. You have giant graphs of object instances- all heap allocated, with tons of pointer chasing. You have collections (not arrays) that we don't have to guess the maximum size of.

If you're storing 20 million small values in an array, then go ahead and use byte[] or whatever. But that should be in some kind of high performance package in the standard library. The "standard" Integer type should err toward correctness over performance- the very same reason Java decided to be "C++ with garbage collection".

I'm also not literally talking about the BigInteger class as it's written today. I'm talking about a hypothetical Java that exists in a parallel universe where the built-in Integer type is just arbitrarily large. It could start with a default size of 4 or 8 bytes, since that is a sane default. Maybe the compiler would have some analysis that sees the number could never actually be large enough to need 4 bytes and just compile it to a short or byte. These things should be immutable anyway, so maybe the plus operator can detect overflow (or better if the JVM could do some kind of lower-level exception mechanism so the happy path is optimized) and upsize the returned value size. Remember, integer overflow doesn't actually happen very often- that's exactly the reason people don't typically complain about it or ever notice it (except me ;)), so it's okay if the JVM burps for a few microseconds on each overflow.

All this doesn't matter because it'll never, ever, actually happen. I just think they made the wrong call and it has unfortunately led to lots of real world bugs. It's hard to right correct, robust, software in Java.

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#207

Earlier quoted context omitted.

> the default should basically be a smart version of BigInteger that maybe the JVM and/or compiler could guesstimate the size of or optimize while running. I suspect this would be disastrous for performance. I believe Haskell uses a similar approach though. Sometimes you want to store 20 million very small values in an array. Forcing use of bigint would preclude doing this efficiently (in the absence of very smart co…

> I suspect this would be disastrous for performance. I believe Haskell uses a similar approach though. > > Sometimes you want to store 20 million very small values in an array. Forcing use of bigint would preclude doing this efficiently (in the absence of very smart compiler optimisations that is). I suspect that it would. I also suspect that I don't care. :p We're talking about Java. Yes, you can write high-perform…

I suspect the performance penalty would be so severe it might undermine the appeal of Java. I don't have hard numbers on this though, perhaps optimising compilers can tame it somewhat. Presumably Haskell does.

A more realistic change might be to have Java default to throwing on overflow. The addExact methods can give this behaviour in Java. In C# it's much more ergonomic: you just use the checked keyword in your source, or else configure the compiler to default to checked arithmetic (i.e. throw-on-exception). This almost certainly brings a performance penalty though.

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#208

Earlier quoted context omitted.

> No unsigned ints. I think that's a mixed blessing. I believe Java did this deliberately to avoid the trouble that C and C++ have with signed and unsigned integer types having to coexist. Personally I've never been inconvenienced by Java's lack of unsigned integer types, but I'm sure it can be annoying in some situations. I'm quite fond of Ada's approach to integer types, but I suspect I'm in a minority. > Silent in…

I don't know about Ada, but I enjoy Rust's strictness when it comes to numeric types. > Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap. EXACTLY. It's f-ing stupid. C's excuse was compilers doing magic on UB or whatever. Java has no such excuse. They just wanted it to behave the same as C/C++ to attr…

> I don't see how const and immutability align with Java's original philosophy of being object-oriented, which is all about opaque objects that control internal mutable state.

That's an interesting point, but an object presents an interface and promises to deliver some particular behaviour. A const system is a way of letting the type-system formalise some of an object's promises, no?

I don't think this is particularly 'leaky' (in the sense of leaky abstractions). Java's String class doesn't let me access its internal character array, but it still matters to me that it promises never to mutate it, nor to let anyone else mutate it (at least ignoring reflection). That's relevant at the level of the interface, not only at the level of the implementation.

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#209
post #29

All the backend projects I work on were originally written in Java. I'll admit that Streams and Lombok make it a somewhat pleasant experience to work with, at least compared to when I used it in college (I think Java 8 was newest?). But that said, I once tried writing a new feature in Kotlin, and now all our code is Kotlin. I don't really see how Java can compete with the non-nullable types, data classes, and type-in…

> data classes, and type-inferencing Kotlin gives you,

You mean records and local variable type inference that are apart of Java?

    record SomeThing(int i, String s) { }


    var some = new SomeThing(2, "blah");

Re: A categorized list of all Java and JVM features since JDK 8 to 16

#210

Earlier quoted context omitted.

OP mentioned "Ada's approach to types", as well. Ada lets you write stuff like "T is range 1 .. 20" or "T is range -1.0 .. 1.0 digits 18". This then gets mapped to the appropriate hardware integer or floating-point type.

Yeah, I've read little snippets like that from blog posts and stuff, but I've never written a single line of Ada, so I really don't know how that works out in practice. What happens if you overflow at runtime? A crash, I assume/hope? My point of view is that this is the opposite of what I'm talking about anyway. Java is a high level language where we are usually writing in Java because we're agreeing to give up a lot…

> What happens if you overflow at runtime? A crash, I assume/hope?

In Ada, if range constraints are broken at runtime, a Constraint_Error is raised (or 'thrown', if you prefer). [0] (That's assuming of course that range checks haven't been disabled, which is an option that Ada compilers offer you.)

> I don't see why the default for Java is to have these really nitty-gritty numeric types

At the risk of retreading our earlier discussion:

I think the short answer is performance. Java has lofty goals of abstraction, yes, but it also aims to be pretty fast. If it didn't, its appeal would diminish considerably, so it's reasonable that they struck a balance like this. Same goes for why primitives aren't objects.

[0] https://learn.adacore.com/courses/Ada_For_The_CPP_Java_Devel...

Post reply on HN