For example, GC escape analysis, automatic lock elision, devirtualization, tiered compilation are fairly recent features in C#, and likely not as mature/powerful.
Or C# has generic specialization, so if your generic param is a stuct, you get a separate implementation, while Java generics work via type deletion.
But in C# you have a ton more synchronization primitives, value types, methods are non-virtual by default etc.
This usually means that expertly crafted C# code can be faster than Java (and more importantly, you can trust the compiler to do the right thing), while if you wrote it exactly like Java, you'd probably end up with slower code.
That's not the case for some time already, at worst you get similar performance with Java and with a little effort you can get significantly better performance.
Operator overloading increases consistency. Instead of having
int a = b + c;
CustomNumberType x = y.add(z);
you have int a = b + c;
CustomNumberType x = y + z;But not having operator overloading when dealing with vectors is maddening.
Person x;
Job y;
CustomType z = x + y;
WTF is Z?Is the argument, anyway, I support operator overloading.
Hopefully a type error, because no sane programmer would implement addition like this. Obviously an insane programmer could, but that’s not the fault of operator overloading. The following code is exactly as confusing:
Person x;
Job y;
CustomType z = add(x, y);Take a person, add a job, you get an Employee or EmployedPerson. Person.add(job).
I agree overloading can create footguns, but domain concepts should make a lot of sense in context when basic arithmetic operations are performed on them. Party = Meeting + Booze.
Custom operators are a big assistance for DSLs, and overloading can also aide their creation and elegance. Part of the “awesome but don’t be a jerk about it” toolbox.
One of the reasons why it took off.
Turns out they really want to have plenty of Java development on Azure as well.
https://javapro.io/2026/03/05/java-25-and-the-new-age-of-per...
https://docs.oracle.com/en/java/javase/26/core/virtual-threa...
Associating virtual threads with "legacy heavy weight threads" is a fundamental misunderstanding
> That’s why Swift, Rust and Typescript all chose async/await for concurrency.
And Java chose to join Team Go/Erlang. At the end of the day, async/await is just syntactic sugar for futures/promises, which are essentially a way out of callback hell.
Besides, Rust and Typescript aren't good examples here: a green-thread scheduler (a runtime component) contradicts Rust's philosophy, while Typescript is inherently constrained by Javascript.
https://github.com/dotnet/runtimelab/issues/2398
Which ended with,
> We have chosen to place the green threads experiment on hold and instead keep improving the existing (async/await) model for developing asynchronous code in .NET. This decision is primarily due to concerns about introducing a new programming model. We can likely provide more value to our users by improving the async model we already have. We will continue to monitor industry trends in this field.
Now three years later we have,
https://github.com/dotnet/core/blob/main/release-notes/11.0/...
> Runtime async is a major runtime feature in .NET 11 that introduces new runtime-level infrastructure for async methods. The goal is to improve tooling and performance for async-heavy codepaths. For more details and to track progress, see the Runtime Async epic issue.
So it's going to be an internal implementation change -- that is, it generates the MSIL code that's generate when a developer writes async/await code, and won't require changes to how a developer writes async/await code?
No, they won't. C# already got itself into a corner with 32 bit arrays and 32 bit spans. And if unions are introduced as reference only that will never be fixed due to binary compatibility requirement.
Java’s Optional sucks compared to how C# (and Kotlin) implement support for nullable types. C#’s async/await syntax is better than… however the hell Java says to implement asynchronous calls now (Thread? CompletableFuture? idk, I never figured it out). ffs, Java doesn’t even have support for string templates yet — they added it as a JDK preview feature (JDK 21?) and then removed it before final release.
Even when I was building stuff in Kotlin, I couldn't figure out how to make async -- coroutines? RX? I forget already -- work, either. But that was all in the last few months before I left & moved back to the .NET stack.