upvote
This is great advice and it applies to a lot more than just language features. Different architecture, deployment setups, QA approaches are all like this. It's always "approach A is no good", "but company X uses approach A and they're doing very well", "yeah but look at all of these problems they have". Maybe a fair argument but the approach B people also have their fair share of problems...
reply
What's funny is that the only languages in the same popularity league as Java are Python and JS/TS (and possibly C and C++ if you want to extend things to more domains) yet I rarely ever hear people saying Java should be more like these languages. It's because many of the people who dislike Java also don't like any of the most popular languages (even those who like, say, Python better don't think Java should be more like Python).

These people may point out that languages become more or less successful not because of the things these people care about but because of other factors. And they're right, but then the question is, shouldn't a smart product team focus more on the things that actually matter more to more people?

Programming languages are tools, and so their value is not intrinsic, but comes from the value of the software they're used to create. Now, some people claim that Java's success is largely the result of it being one of the most hyped languages of the late 90s and early 00s, alongside VB, Delphi, FoxPro, and C#. But this claim doesn't stand up to even the slightest scrutiny.

reply
GUI frameworks have similar arguments around their design choices.
reply
> All of the language differences between .NET and Java fall in this "non-consensus" zone

Curious if you think fibers vs async/await is still in this zone (amongst experts). It seems fibers are objectively better. But I'm no expert*

reply
I think user-mode threads are better when the language already has threads. I'm not sure they would have been better for JS. But yeah, this one is probably less controversial than things like properties and extension members, which we also said "yeah, nah" to.
reply
Value types kind of definitively don't have null, right? You can have a zero int but not a null int. So nullability is not entirely orthogonal to value types, its an advantage for value types where they are practical.
reply
I didn't say nullability is orthogonal to value types; I said it was orthogonal to the two-projections world, which is what that text in the article was about rather than nullability.

As to value types and null, I'm not sure about the current picture, but the general idea is that you declare what semantic properties you want - identity or not, nullable or not, tearable or not - and then the compiler picks the best technical in-memory representation for each use. For example, the compiler could choose not to flatten variables that could be null in the heap but to flatten them in the stack. That's the general idea, but I'm not sure about the details, some of which may yet change.

More generally than just Java, nullability is often a property not of a type but of a variable. For example, in C, an int may not be null, but a pointer to an int may be. Now, in C, `int` and `int*` are two different types, but that's exactly a distinction that the original projection-spit design made and we wanted to avoid. But you could still end up with a variable that could hold either an integer or a null and another that may hold an integer but not a null, only this is separate from the reference/value projection, which combines both identity and nullability (in C, `int*` is not only nullable, but also has identity).

reply
> More generally than just Java, nullability is often a property not of a type but of a variable

I'm going to hard disagree here. And the syntax proposed in the Null-Restricted Value Class Types JEP is a major step backwards.

I want to banish nulls from my codebase, completely. I can currently do this with a variety of annotations (at the package-info.java level) and tooling, though it's not integrated well with the language.

Forcing exclamation marks into every variable and parameter is a lot of annoying noise that quite simply nobody will do. The default should be non-nullable, especially for value types.

Declaring whole types as non-nullable is less noisy and errorprone than annotating every variable declaration. If you aren't going to give me "declare the whole codebase as non-nullable" then at least give me something coarse-grained.

reply
> the syntax proposed in the Null-Restricted Value Class Types JEP is a major step backwards. Forcing exclamation marks into every variable and parameter is a lot of annoying noise that quite simply nobody will do. The default should be non-nullable, especially for value types.

Then you didn't read the JEP draft (it's not an accepted JEP) carefully. It says, under "future work":

Providing a mechanism in the language to assert that all types in a certain context are implicitly null-restricted, without requiring the programmer to use explicit ! symbols.

In other words, the draft already incorporates your point, but JEPs (both drafts and actual JEPs) follow the pattern we've found to work so well, that features are best delivered piecemeal rather than in a big bang.

Having said that, I don't know the current plans for this matter, as that document is only in Draft status, so saying it's good or bad is pointless, as it's not even a proposal yet, just something being explored.

reply
It's a proposal for a proposal, fine. Consider this my proposed feedback.

I don't think this syntax is desirable as currently proposed, and that one line under "future work" is doing far too much lifting. My sincere hope is that there are people closer to the process that also feel this way, they will provide similar feedback, and the next draft will be something completely different.

reply
> More generally than just Java, nullability is often a property not of a type but of a variable.

This is a tangent, but I'm not sure I follow this. Can you give an example to make this clear?

reply
Yes, but it comes from Java having both runtime and compile-time types; it's harder to make the distinction in languages that don't have runtime types.

In Java, you can ask, `x instanceof T` (and this is a runtime test), which means, is x one of the values in the set of values allowed by T. `null instanceof Integer` is false, even though a variable of type `Integer` can be assigned a null. So you can think of `Integer x` as being `Integer|null x`, i.e. x can hold a null, even though `null instancof Integer` is false.

reply
I think I mostly got this, but just to test it, it would be like in Typescript where I might say:

    type Foo = { x: number; }
    type Bar = { x: number; y: number }
    type FooBar = Foo | Bar;
    function baz(x: FooBar) {
      if ('y' in x) {
        // compiler now knows x is a Bar
      }
    }
In this case, the variable `x` has a property that is determined by the compiler based on control flow. i.e. it isn't explicitly carried by the type of `x`.
reply
In case you want to edit it back in: in the 3rd paragraph second sentence, the star in your int* got gobble up by formatting to italics.
reply
You can have a null int, it's called Integer.

What was taken away is the other, identity-having functionality of Integer and similar (e.g. no synchronization).

reply
This won't be true in Java, though - in Java, you will have null Integers at least. It seems that int will remain a different thing entirely from Integer, and will remain a JVM-only concept.
reply
But with null-restricted types, Integer! and int has no difference semantically and representation. They plan to introduce null-restricted types in future.
reply
What's wrong with what .NET did with threads? Having async tasks sharing the GUI thread seems like a nice feature. Will we be able to use virtual threads and structured concurrency with Swing, e.g. to wait for a background task in an event listener?
reply
Regarding "What's wrong with what .NET did with threads?", see https://cr.openjdk.org/~rpressler/loom/Loom-Proposal.html (relevant part below):

  An alternative solution to that of fibers to concurrency's simplicity vs. performance issue is known as async/await, and has been adopted by C# and Node.js, and will likely be adopted by standard JavaScript. Continuations and fibers dominate async/await in the sense that async/await is easily implemented with continuations (in fact, it can be implemented with a weak form of delimited continuations known as stackless continuations, that don't capture an entire call-stack but only the local context of a single subroutine), but not vice-versa.

  While implementing async/await is easier than full-blown continuations and fibers, that solution falls far too short of addressing the problem. While async/await makes code simpler and gives it the appearance of normal, sequential code, like asynchronous code it still requires significant changes to existing code, explicit support in libraries, and does not interoperate well with synchronous code. In other words, it does not solve what's known as the "colored function" problem.
Regarding Swing, virtual threads are "just" threads so no reason they (and structured concurrency) can't be used.
reply
So does Java provide an API for continuations or just the virtual threads hidden behind the threading API? You can't use threads to wait for something on the UI thread (which is why e.g. SwingWorker exists), but you can with await.
reply
The colorless functions approach has well-known disadvantages though, including providing less control and making interop a pain. It isn't like one approach is the correct one and another is a mistake.
reply