What kills me more is originally when they released Kotlin, and I saw Kotlin Native, I assumed they would go all in on Kotlin Native, and allow you to produce fully native JRE based apps (JRE based in the sense that it can take full advantage of those libraries the JRE provides, or any plain old Java Object) and produce native and highly performant binaries.
It seems .NET already has a really mature AOT, I'm really hoping .NETs AOT reaches the point where all .NET code can be AOT'd someday.
I feel like Kotlin could do so much more, but its stuck in standstill. There's even some language features that are still missing such as Inline Classes, Pattern Matching, and even Reflection, all things that Java supports directly.
Programming languages are high-cost, low-revenue beasts. When Kotlin was first released, JB was hoping it would replace Java in the enterprise. And as the sole providers of Kotlin tools, they'd have a greatly expanded market.
Kotlin did not replace Java, except on Android. So JB now has a beast they have to feed without an enterprise revenue stream. They do get secondary benefits: they use it internally, etc.
We examined Kotlin in detail for a CLI app and based on conversations with Kotlin developers concluded that it was not sufficient of a Java replacement for us to evaluate further. For those in a similar situation, the greatly increased cadence of Java releases has probably permanently foreclosed Kotlin-qua-enterprise language.
The irony is that there are a lot of "XYZ Native" solution, such as Flutter, Kotlin Multiplatform, Xaramin, etc. But somehow only React Native, something based on a web framework, seems to reach the critical mass.
I don't really see any cross platform framework hitting "critical mass". React Native is very accessible to web developers. Are you sure that is not just information bias from your own situation? For me, most cross platform solutions are a race down to the lowest common denominator (and this is from someone who has used Xamarin Forms and MAUI in production apps.) Sure, you get to use your favourite tech stack (for us it was .Net) and you get familiar tools and metaphors, but the overall results are very uncanny valley unless you put a lot of effort in to styling for each platform.
What did you mean by "inline classes" here? My only understanding of that term applies to something like what Java is still developing for Valhalla but has not delivered yet, whereas Kotlin is arguably a little farther along with its own feature with that exact name.
Would it be the classes Java has always had that sit inside a parent class and can access the instance fields/properties of the parent? These are very Java, and hard to recreate in a language that doesn't support that type of embedding. C# really struggles with it. Using the Android API via Xamarin was always very much harder to do anything with because of this symbiotic relationship. I have done a little Kotlin, but I don't remember if it support that or not.
There are fundamental restrictions where NativeAOT will never work or be desirable. For example, runtime-compiled regex patterns or any other feature which relies on emitting IL at runtime and creating new members or even assembles and then loading them. Similar applies to compiled expression trees - they are supported in NativeAOT but in the form of falling back onto interpreting them, which has worse performance. Or unbound reflection with patterns that cannot be statically proven and/or analyzed.
Reflection analysis can (and will) be improved but there are hard constraints - a correctly working expression like 'someAssembly.GetType(Console.ReadLine())' by definition would have to root (and force compilation for) every type in the assembly, which is highly undesirable or even sometimes unfeasible for AOT compilation. And there is a lot of code which does exactly this.
The main challenge are packages and frameworks. ASP.NET Core is largely compatible (via minimal API) and so is AvaloniaUI, EF Core has some compatibility assurances and DapperAOT is tailor-made as the name implies, serialization is also a solved problem although you may need to use a different API.
At the end of the day, NativeAOT is not something "to be fully migrated to" because it has fundamental restrictions (some of which also affect other languages like Rust or Go) and having JIT around is a feature for patterns which specifically exploit it but is also a performance optimization (DynamicPGO, better instruction selection especially around SIMD paths, turning static readonly's into JIT constants and apply subsequent optimizations on top of that, this is what makes C# port of Mimalloc so good as it elides dead code with assertions impossible to remove dynamically in C/C++). NativeAOT has its own optimizations, and it will continue to diverge with JIT (e.g. there's a toggle in .NET compiler to repeat some optimization phases, usually it's too expensive for JIT but for AOT it's a good fit, AFAIK there is work to productize this).
The wide perception that JIT-compiled code has to be slower stems from other sources of performance overhead that are typical to languages which happen to use JIT (many of which have "weaker" compilers too), not from the JIT compilation itself. There are technicalities like certain calls have to be indirect in order to support patching the callee address, or inter-procedural analysis which is trivial to prove under AOT may not be so under JIT where new callers/callees may be constructed dynamically or a reJIT invoked which would invalidate the analysis results. JIT also costs additional memory. But it's not a source of worse performance.
It seems .NET already has a really mature AOT, I'm really hoping .NETs AOT reaches the point where all .NET code can be AOT'd someday.
I feel like Kotlin could do so much more, but its stuck in standstill. There's even some language features that are still missing such as Inline Classes, Pattern Matching, and even Reflection, all things that Java supports directly.