https://shipilev.net/jvm/anatomy-quarks/17-trust-nonstatic-f... is a damned shame. User code misses out on an important optimization available only to system-provided classes because certain frameworks have abused JNI and reflection to mutate final fields, which by all rights should be immutable.
Platforms, especially compilers and runtimes, need to be absolutely strict in enforcing semantic restrictions so as to preserve optimization opportunities for the future.
As part of our "integrity by default" strategy [1] we're changing that. There will be a JEP about it soon.
The idea is that because not much code actually needs to mutate finals (and even if it does, that operation is already limited today to classes in the code's own modules or ones explicitly "open" to it), the application will need to grant a permission to a module that wants to mutate finals, similar to how we've recently done things with native calls and unsafe memory access.
I wonder if it thanks to some people blindly following Effective Java book that made a sin by saying "final all the things". So now we cannot easily mock final classes in tests. And mocking tools have to resort to bytecode manipulation to mock the final classes.
E.g. Effective Java is a requirement inside Google, so even public GDrive APIs have final classes. External APIs is exactly the thing you'd want to mock.
Field modifiers are a semantic constraint not a security constraint. It is right and proper that you should be able to bypass them with the appropriate backflips.
The main issue is safety cause you might modify something that isn’t modifiable and cause a SEGV and that is precisely the concern access modifiers are meant to address.
I’ll be the first to admit that I’ve written the evil three liner to “un-final”, mutate, re-final a member off in some long forgotten internal library to dodge a gnarly refactor.
I do wish that I couldn’t have done so, shrug, business needs
Yes, but you would be surprised how many people want to change static final fields for various reasons - be it testing, or other things.
When telling those that it doesn't work, and that it can not work without violating the semantics of the JVM, they will wave their hand and say "look, it does work here". And it looks like, yes, if the stars align in that specific constellation, it may work.
I find the "modern" (if I can call it that) approach to cross-platform interesting: interoperability between languages makes it possible to share a library between multiple platforms when it makes sense. Until now I was exclusively doing that with C++ (possibly with a C API), but obviously C++ is never the preferred language when it is itself not necessary.
My concern, however, is about the cost of doing this. Say I have an easy way to call my Kotlin library from Swift in a mobile app, doesn't it mean that now my iOS app will load some kind of JVM (I don't know what would run on iOS)? Similarly, if I could call Swift from an Android app, wouldn't it load some kind of Swift runtime? It all brings overhead, right?
I guess I fear that someday, developers will routinely depend on e.g. a Swift library, that will depend on a Kotlin library (loading some JVM), that will itself use JNI to call some C++. Just like with modern package managers, programs quickly end up having 100+ transitive dependencies (even with just a few direct dependencies) just because it was "too easy" for the developer not to care.
If you work for a few years with JVM based languages this set of articles are so interesting! I remember reading through these for the first time several years ago.
I’ve basically forgotten about Java. It would never occur to me to start a new project in it. Am I the only one? It feels like I’d reach for python if I want fast development and flexibility, Go if I want to handle a bunch of I/O concurrency in a garbage collected way, Swift if I want a nice language that’s compiled and balanced, or Rust if I want performance and safety in a compiled language. Those are just my personal leanings. I know kotlin has made Java more ergonomic, and yet….
You're not the only one I'm sure, but sounds like you don't need it. Its major strengths are:
• Bottomless resource of developers with Java experience
• Vast array of existing libraries many of which are enterprise focused
• Managing very large codebases with many contributors is straightforward
• Standard VM that's very solid (decades of development), reasonably fast, and supported on essentially all platforms.
It doesn't have quite the stranglehold (even in Enterprise) that it had in perhaps the early 2000s and it's the archetypical "blub" language, but it's a perfectly reasonable language to choose if you're expecting Enterprise scale and pure performance is less valuable to you than scaling out with large numbers of developers.
I like Rust, but it's Java that puts bread on my table.
In addition to other answers: Java absolutely does have the qualities necessary for startups, so using it for new projects makes sense.
1. Modern frameworks and AI assistance can help ramping up a decent backend in days. Solo tech co-founder needs to know only Java or Kotlin and some frontend stack to build MVP quickly and will spend such amount of time on non-coding tasks where language features will be irrelevant. Swift can be the second language if you go mobile-native.
2. Scaling isn’t the problem you are going to have for quite a while. It is quite likely, that problem of scaling the team will come first and any performance bottlenecks will be noticeable much later. Java is good for large teams.
That said, from business perspective, if you want larger talent pool, fast delivery cycles and something that may remain as your core stack in the long term - Java or Kotlin is probably the best choice. If you want fancy tech as a perk to attract certain cohort of developers or you have that rare business case, you can choose Go or Rust. Python is popular in academia and bootcamps, but TBH I struggle to see the business value of it for generic backends.
Java is better than Go on every count, and almost all of your cases are 90% done by Java, so it's quite clearly a very good choice for almost everything.
I think it's not just you, but certainly not everyone. Kotlin with Java 21+ is my go-to choice for an I/O-bound service, or really any service. It's just so ergonomic, and with virtual threads the code can be as simple and efficient as Go - while also taking advantage of possibly the best and largest library ecosystem in the world.
I'm not knocking Go or Python - if those are your preferred tools, they're more than adequate. Java, however, isn't nearly as irrelevant as you may perceive.
It's an interesting parallel development that people are complaining about the bad original language on both JS and JVM platforms and often using other languages (Kotlin, TypeScript, Clojure/ClojureScript, etc). I guess even Swift instead of Objective-C on the Apple side counts here in a way.
Probably not. Java had stagnated for quite a while, entirely missing the lightweight threading and/or async/await revolution of the last decade. The JVM ergonomics also just sucks, a lot of apps _still_ have to use -Xmx switches to allocate the RAM, as if we're still using a freaking Macintosh System 6!
On the other hand, it's a very mature ecosystem with plenty of established battle-tested libraries.
quotemstr|1 year ago
Platforms, especially compilers and runtimes, need to be absolutely strict in enforcing semantic restrictions so as to preserve optimization opportunities for the future.
pron|1 year ago
The idea is that because not much code actually needs to mutate finals (and even if it does, that operation is already limited today to classes in the code's own modules or ones explicitly "open" to it), the application will need to grant a permission to a module that wants to mutate finals, similar to how we've recently done things with native calls and unsafe memory access.
[1]: https://openjdk.org/jeps/8305968
deepsun|1 year ago
E.g. Effective Java is a requirement inside Google, so even public GDrive APIs have final classes. External APIs is exactly the thing you'd want to mock.
blibble|1 year ago
https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.htm...
given this it's not surprising others thought it was acceptable also
rusk|1 year ago
The main issue is safety cause you might modify something that isn’t modifiable and cause a SEGV and that is precisely the concern access modifiers are meant to address.
bobnamob|1 year ago
I do wish that I couldn’t have done so, shrug, business needs
J-Kuhn|1 year ago
When telling those that it doesn't work, and that it can not work without violating the semantics of the JVM, they will wave their hand and say "look, it does work here". And it looks like, yes, if the stars align in that specific constellation, it may work.
elric|1 year ago
lukeh|1 year ago
https://github.com/swiftlang/swift-java
palata|1 year ago
My concern, however, is about the cost of doing this. Say I have an easy way to call my Kotlin library from Swift in a mobile app, doesn't it mean that now my iOS app will load some kind of JVM (I don't know what would run on iOS)? Similarly, if I could call Swift from an Android app, wouldn't it load some kind of Swift runtime? It all brings overhead, right?
I guess I fear that someday, developers will routinely depend on e.g. a Swift library, that will depend on a Kotlin library (loading some JVM), that will itself use JNI to call some C++. Just like with modern package managers, programs quickly end up having 100+ transitive dependencies (even with just a few direct dependencies) just because it was "too easy" for the developer not to care.
lsuresh|1 year ago
This article about the "stack allocation" misnomer in Java in particular is one of my favorites: https://shipilev.net/jvm/anatomy-quarks/18-scalar-replacemen.... What the JVM really does is escape analysis + scalar replacement.
exabrial|1 year ago
plandis|1 year ago
wging|1 year ago
aardvark179|1 year ago
unknown|1 year ago
[deleted]
unknown|1 year ago
[deleted]
azinman2|1 year ago
dcminter|1 year ago
• Bottomless resource of developers with Java experience
• Vast array of existing libraries many of which are enterprise focused
• Managing very large codebases with many contributors is straightforward
• Standard VM that's very solid (decades of development), reasonably fast, and supported on essentially all platforms.
It doesn't have quite the stranglehold (even in Enterprise) that it had in perhaps the early 2000s and it's the archetypical "blub" language, but it's a perfectly reasonable language to choose if you're expecting Enterprise scale and pure performance is less valuable to you than scaling out with large numbers of developers.
I like Rust, but it's Java that puts bread on my table.
ivan_gammel|1 year ago
1. Modern frameworks and AI assistance can help ramping up a decent backend in days. Solo tech co-founder needs to know only Java or Kotlin and some frontend stack to build MVP quickly and will spend such amount of time on non-coding tasks where language features will be irrelevant. Swift can be the second language if you go mobile-native.
2. Scaling isn’t the problem you are going to have for quite a while. It is quite likely, that problem of scaling the team will come first and any performance bottlenecks will be noticeable much later. Java is good for large teams.
That said, from business perspective, if you want larger talent pool, fast delivery cycles and something that may remain as your core stack in the long term - Java or Kotlin is probably the best choice. If you want fancy tech as a perk to attract certain cohort of developers or you have that rare business case, you can choose Go or Rust. Python is popular in academia and bootcamps, but TBH I struggle to see the business value of it for generic backends.
mpenet|1 year ago
I wouldn’t dismiss the JVM as a whole, it is a marvel of engineering and is evolving quickly nowadays (see loom, panama, leyden, etc…).
kaba0|1 year ago
Java is better than Go on every count, and almost all of your cases are 90% done by Java, so it's quite clearly a very good choice for almost everything.
pianoben|1 year ago
I'm not knocking Go or Python - if those are your preferred tools, they're more than adequate. Java, however, isn't nearly as irrelevant as you may perceive.
fulafel|1 year ago
izacus|1 year ago
Is this posturing? Do you feel cool? Why did you come here and bloviate over something as silly as a language choice?
palata|1 year ago
I think anything JVM (be it Java, Kotlin or Scala) would be very good there. A lot better than ElectronJS.
cyberax|1 year ago
Probably not. Java had stagnated for quite a while, entirely missing the lightweight threading and/or async/await revolution of the last decade. The JVM ergonomics also just sucks, a lot of apps _still_ have to use -Xmx switches to allocate the RAM, as if we're still using a freaking Macintosh System 6!
On the other hand, it's a very mature ecosystem with plenty of established battle-tested libraries.