Love It or Hate It, Java Continues to Evolve
azul.com
azul.com
Once I show them Java 11 with var keyword, lambda's and streams they start appreciating how modern the language has become. Then you throw in frameworks like javalin or sparkjava and suddenly they are not as hostile as they were before.
Honestly the only things I would really like out of Java these days are data classes and possibly native runtime compilation out of the box. Containers solve the latter issue, but id love to just build a binary and run it.
Do that and its on the same level as Go in many ways which nailed the tooling and deployment model, even if the language has issues. Java already has the tooling.
I suppose a decent syntax highlighter library like pygments or chroma would be nice too.
You mentioned Go, so I'd like to add that Project Loom is the OpenJDK initiative to add green threads/coroutines to the JVM which I'd love. The tricky thing with "data classes" are that everyone wants something similar but different between the various terms: records, data classes, value types.
Personally, what I want from this is essentially Lombock-style all-args constructors, equals, hashcode, and toString. Struct-like like embeddability and flat object-headerless arrays are cool but honestly more niche. I don't mind sucking it up and using unsafe.
I don't mind what type of data classes they come up with. I just want to not have to write stupidly long constructors, multiple sets or chain-able objects when I want to populate some fields.
Green threads are nice because they let me separate logical execution from operating system resources. Unlike Go, Java will expose the ability to use a custom scheduler, so that should be nice!
My personal pet usecase is actually the opposite. I want to isolate 5-10 "low priority" tasks to a single kernel thread to isolate how much damage misbehaving code can do.
Yes, you can change the thread stack size, but that's a global fixed size for all threads, so you can't make it to small, or you'll risk stack overflows.
Meanwhile, Go is using dynamically growing stacks, starting at 2k.
[1] https://cr.openjdk.java.net/~briangoetz/amber/datum.html
I started learning Perl in 2007; after it was no longer popular, but also around the time that the principles/practices that became 'Modern Perl' were becoming popular in the community.
I think there is a difference between the two though. Java was always a language which required a lot of ceremony and typing to get anything done at all. Perl was always focused on expressivity. Modern Perl was a case of learning from past mistakes and consciously avoiding some of the more egregious code the language easily afforded. Modern Java is more a case of Java playing catch-up to the more expressive languages out there.
One is applying constraints on your coding style, the other is trying to lift constraints. Both are trying to reach some middle point.
But! I agree that no company writes purely 'Modern Perl' - you will have to deal with some terrifying code at some point.
So. I agree with the concept of the 'Modern X' fallacy, but I would say that dealing with non-modern code is pretty normal, and people who are OK putting up with the pre-modern code may be some of your best employees. I suffer from rewrite-it-all syndrome myself sometimes, but it's something I try to avoid. Code that frustrates me, isn't necessarily bad.
(If it helps contextually, I'm - at heart - a Clojure programmer.)
It's true that post 8 Java becomes palatable. And someone on reddit just showed me this piece of stats:
https://www.jetbrains.com/lp/devecosystem-2019/java/
83% on java 8, java 11 nicely going up
I pity those forced to work with old techniques
I’d much rather have explicit boilerplate code (preferably in library form as much as possible) than complex, invisible magic.
The latter is “easy”, but the former is “simple”. In most cases, “simple” is more important.
haskell has magic, but at least its concise..
I think it's a fundamentally different mentality as far as the code. The codebase is so obfuscated that it might as well be binary data rather than plaintext files. You need an IDE to manipulate it efficiently.
http://jeffacubed.com/the-boilermaker-story-or-knowing-where...
Does not spending time on boilerplate improve the quality of software you produce? I find that it doesn't.
java modules broke a lot of software that were using sun classes or did classloading magic.
I was on one of such application, it's not a lot of effort to migrate forward, but many of the issues are runtime only so unless you have a good test suite, a strong incentive and no closed source library that use some such classes you can't really move forward with it.
anyway, in java 7 was already possible to write "unbloated" code, especially with the servlet 3.0+ spec, but spring and osgi have a lot of inertia in the enterprise community, meaning most people were stuck programming in xml, one of the most unpleasant experience ever.
ps: also how many shops are doing 'migration off of spring' ?
Meh.. in my opinion a lot of this is just tropes. Spring is a fine environment to work in, although you really don't have to use any XML with modern Spring. But you can if you prefer to for some reason.
All the annotation driven "black magic" can be mildly annoying at times, especially before you understand what's going on under the hood... but considering the amount of boiler-plate crap it saves you from dealing with, I'll happily accept that tradeoff.
For my money, if I was starting a new backend service today, I'd absolutely reach for Spring Boot as my starting point. Of course, I may be biased given that I've been writing Java code for 20 years, and I remember what a breath of fresh air Spring was compared to the old EJB 2.x era "J2EE" stuff.
could have had worse you started just around the xdoclet, that was peak madness
is it allowed to produce libs in jars from non java languages ? Even though I don't think clojure/kotlin/else support jre5 out of the box anymore.
So I have to build this as a special type of jar and then set up this other, rather opaque, set of stuff and deal with all sorts of nonsense ...
Now?
get("/hello", (a,b) -> "Hello World");
And we're done...The applications certainly got a lot more complex than that, but we never found ourselves in need of a magic annotation that you can't step through with your debugger.
This is all about simplicity. It's pretty clear how a get() function on the router which takes a route and a callback works, but it requires some work to understand how Spring magically scans all your classes looking for annotations and then builds parser based on that. It's quite unclear where all of the routes are coming from (since they can come from a class just about anywhere). You suddenly need special tools. And the worst thing that suffers is transparency. Ctrl-clicking/Cmd-clicking on an annotation in your favourite IDE never tells you what it does.
In comparison, a second-order function is a vastly simpler and more grkoable beast.
The point was just to show that the http server aspect of it is trivial to set up.
You can hook in whatever you want from there.
object MinimalApplication extends cask.MainRoutes{
@cask.get("/")
def hello() = "Hello World!"
initialize()
}
API design shamelessly stolen from Python's Flask framework @GetMapping("/hello") fun hello() = "Hello, World!" import static spark.Spark.get;
public class HelloWorld {
public static void main(String[] args) {
get("/hello", (req, res) -> "Hello World");
}
}
[1] http://sparkjava.com/ @SpringBootApplication
@RestController
class DemoApplication{
@GetMapping("/hello") fun hello() = "Hello, World!"
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
And this taught me that the best sinatra clone in java isn't really worth it in terms of lines of code saved (we save what? 3 lines of code? 6 with the imports?) when compared to a full-blown web framework like spring boot.Would you care to do another one where we return a json response? I'd bet you will need a json mapper class for complex objects whereas I will return a dataclass instance and it will just work.
fun main(args: Array<String>) = run(*args) {
get { "Hello Kotlin" }
}
It's always easy to make a framework lightweight. But there is no argument that Spring requires a lot of tedious boilerplate in Java (some of which can be avoided with Kotlin), and annotations which are hard to understand.Lightweight frameworks don't really tell you "We do everything that Spring does in less lines", since at one point you'll ask them to make avocado toasts or something else that spring-bakery has a plugin for.
The real power of lightweight frameworks is their simplicity. This is not for everyone (that's why we've got Spring and Rails), but if everybody was fine with the tradeoffs that Spring makes, you'll see the same kind of frameworks ruling the Node.js and Go landscape.
The annotations I used were "this is a spring boot application" on top of a class and another which says "this is a rest controller" to start your hello world application and we only saved a few lines and introduced additional hidden complexity with sparkjava because we still don't have a way to handle json whereas it comes free with spring boot.
You've got another one that says "this is an injectable service" and another which says "this is a db entity" and with the "this is a configuration" you've got 99% of spring boot applications covered. Not sure what is so hard about the annotations. Maybe it has to do with "knowing more than just java/kotlin syntax" that puts people off?
Your Spring example immediately turns me off because it uses a bunch of annotations that could do pretty much anything (and I'd have to learn). Further, it uses kotlin which is not java - it's a valid choice but it's a more terse language in general (so not necessarily valid to compare against java examples).
Spark is straightforward and easy to read, it does one thing and it does it well, and it doesn't demand that I spend a lot of time thinking about anything other than what I'm trying to achieve. I have a lot of time for that.
For the record, with kotlin and skipping imports (of which there would only be one anyway) -
fun main(args: Array<String>) {
get("/hello") { req, res -> "Hello World" }
}
I do indeed find this simpler than your proposal upthread.This behavior which looks helpful at first glance, is very confusing. If your not proficient with Spring or spend hours reading the documentation you'll never understand that: 1. Your object lifetime is tied to the request. 2. The instance you see at the debugger is actually a subclass that redirects all call to the actual scoped object instance depending on a thread local variable (I guess?) 3. The proxy instance is either slow (JDK Proxy) or could cause you grief when you upgrade to the next version of Java (CGLIB). It's not clear which is which without some investigating.
I rather prefer to go with a lightweight framework + a sane DI framework and do something more explicit, e.g. create a RequestProvider<T> interface:
interface RequestProvider<T> {
get(request: Request): T
}
class MyController() {
val fooProvider: RequestProvider<Foo> by inject(name = "foo")
fun myHandler(request: Request) {
val requestScopedFoo = fooProvider.get(request)
}
}
This approach is more explicit, but I prefer it.How exactly does Guice differ in request- or session-scoped injections that makes it that much better?
I have no need for one of those, I'm good building some fairly simple microservices against spark without 'lazy client initialization' or any framework features.
That's kinda the point, it's a simpler toolset for a single purpose. You may have requirements that are made simpler for you by knowing the arcane ins and outs of a feature-rich framework.
Not everyone does, and bringing in said framework to achieve something simple in the name of "Maybe, in future, I may need this" is a prime route to an overcomplex, heavyweight mess. IMHO.
Maybe you've avoided Spring-related hell, but all the java devs I know who survived that era tend to groan whenever it's mentioned.
I wrote a lot of Java, but it was a long time ago. Could you share a bit of code representative of the modern Java 11 style you mentioned (var keyword, lambdas, streams, etc.)?
public static void assertIsSuperset(Collection<Object> superSet, Collection<Object> actualSet) {
final List<Object> missing;
missing = actualSet.stream().filter(x -> !superSet.contains(x)).collect(Collectors.toList());
if (missing.size() != 0) {
/* Imagine some more verbose exception creation here, which is why we caught it in a list*/
}
}
The lambda is in the creation of 'missing': filter(x -> !superSet.contains(x))
A lot collections in Java 8 brought in a stream method which has some functional esqe methods. Filter is one of them, and if you look at filter's method signature: public interface Stream<T> extends BaseStream<T, Stream<T>> {
/**
* Returns a stream consisting of the elements of this stream that match
* the given predicate.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* predicate to apply to each element to determine if it
* should be included
* @return the new stream
*/
Stream<T> filter(Predicate<? super T> predicate);
[SNIP!]
}
Predicate is an interface that has one method: 'public boolean test(T t);'. The lambda that I created conforms to that single method; accepting the generic object (x) and returning the boolean tested by '!superSet.contain(x)' The other way I've seen them used is to implement an anonymous version of an interface. public static void assertIsSuperset(Collection<Object> superSet, Collection<Object> actualSet) {
var missing = actualSet.stream().filter(x -> !superSet.contains(x)).collect(Collectors.toList());
if (missing.size() != 0) {
/* Imagine some more verbose exception creation here, which is why we caught it in a list*/
}
} func isSuperset<Type: Equatable>(superSet: Set<Type>, actualSet: Set<Type>) -> Bool {
return actualSet.contains(where: { !superSet.contains($0) })
}
What is all the noise about "collect"? Why allocate a while new list? What's with the "final"? And why do I have to type out obvious types in 2019? return superSet.containsAll(actualSet);
But a straight up boolean doesn't tell me what is missing, only that something is missing. I snipped out the bit about creating a more verbose exception creation because it wasn't really needed for the example:if (missing.size() != 0) { final StringBuilder builder; builder = new StringBuilder(); builder.append("Missing item found: [ "); builder.append(String.join(",", missing.stream().map(x -> x.toString())).collect(Collectors.toList()))); builder.append(" ]"); throw new IllegalStateException(builder.toString()); }
Keep in mind that the objects that this method was written for actually implement human readable toString methods.
> What's with the "final"?
Habit that I forced on myself. But it's not a bad habit to get into. In theory methods are supposed to be short and readable but reality often ends up being that in the fury of writing under looming deadlines you can have horrifically long methods that do many things. And I've seen more then a few times someone reusing variable names badly. Using final when possible is just a way for someone to be able to look at the code, see the variable name and be able to know that what it says is what it is.
>And why do I have to type out obvious types in 2019
Because types aren't always obvious. That's a list, meaning that you can have duplicated items and that order matters. What if my 'list' was actually a set, in which order was not guaranteed? What about if you're trying to integrate this with multiples teams scattered across the world that don't speak English on a code base that's about 500,000 lines of code? Or when you walk away for 2 or 3 years and then have to come back to a method that's not as small and trivial as the given example? What about the next poor sap that has it dumped in his lap?
I know it seems stupid in the short run, and if you've got something small or something that probably wont' be relevant in a year or two, and I'd agree with you if that's the case. But but if you've got a code base that needs to be communicated to other people, then typing is a good way of handling that communication without having to write extra documentation.
Linux support is still WIP, Windows nowhere to be seen, and everything else is not supported at all.
actualSet.stream().anyMatch(x -> !superSet.contains(x));
I have a Map of Lists of Foo items (foos), and I want to transform each item in the sublists to a Bar, filter out bad Bars, transform to Baz and keep the results as a new list of Bazs
List<Baz> barList = foos.values()
.stream()
.flatMap(List::stream)
.map(Foo::transformToBar)
.filter(Bar::isBad)
.map(Baz::transformToBaz)
.collect(Collectors.toList());
The syntax is a little clunky at times... but once you get used to it it's quite expressive.Java's problem is that for many years it used to be the slowest car around. Maybe this was one of the things that helped Java become the undisputed ruler Enterprise Inc. but now it's got a lot to catch up with, and even though the pace has picked up since Java 9, it's far behind its competitors.
Everything you mention (var, lambdas, streams) and everything you might mention when Java 13 comes out (switch expressions, text blocks) is just too little, too late.
Yeah, var is a lifesaver if you have to use Java, but compare the limited type inference it brings to the table with almost any other competing language (Kotlin, C#, Scala or Swift) which at the very least can do a return value inference.
Likewise, streams are long due, but the syntax is horrible and the repertoire of built-in operations is meager, and since Java does not support extension functions like the rest of the languages mentioned here you can't extend them gracefully.
If you take one of the examples given here:
var grades = school.students()
.stream()
.flatMap(student -> student.grades()
.stream()
.map(grade -> new NamedGrade(student.name(), student.grades())
.filter(x -> x.grade() > 60)
.collect(Collectors.toList());
Kotlin goes the same route, but makes it little more natural to use: val grades = school.students
.flatMap { (name, grades) -> grades.map {
NamedGrade(name, it)
}}
.filter { it.grade() > 60 }
.toList()
Besides simply more natural syntax, get the special 'it' variable, destructuring, more operations andNow take C#, a language had something "Streams" since version 3.0 (released 7 years before Java 8):
var result = students
.SelectMany(student => student.Grades.Select(grade => new { Name = student.Name, Grade = grade }))
.Where(record => record.Grade > 60);
Where you get anonymous records - you don't need to define a new type anymore.Or with LINQ:
var result = from student in students select
from grade in student.Grades
where grade > 60
select new { Name = student.Name, Grade = grade };
You get a highly readable built-in query language as well.And neither Java nor C# have anything to brag about Streams or LINQ.
I could already writen those code examples in Lisp, Scheme, Caml Light or Smalltalk.
What we have is mainstream computing catching up, or as Erik Meyer has stated, selling Haskell to VB programmers.
Who started using Java way before Java 8, used to experience serious issues about memory usage and performances in the JVM.
These memories struggle to leave.
I built my profitable, bootstrapped B2B SaaS using sparkjava.
You can find sparkjava here: http://sparkjava.com/
Where Spring shines is enterprise. When you have 600k concurrent users it's just something I can trust. Other solutions can work too, but we value the stability.
I agree with modern Java improvements for the most part, especially if you are including Kotlin in that ecosystem - I really enjoy writing Kotlin.
However, I've professionally switched to Go and really wouldn't want to go back to Java much. I already think Go is a bit bloated, and I still feel like Java (even w/ the latest modern features) still gets incredibly bloated.
Could you clarify this? I last worked on Java in 2016 but even at the time, we had eclipse settings that had Syntax highlighters. Am I missing something?
You can use Pygments in Java using Jython but its performance is questionable and can be incredibly slow when highlighting lisp code for example.
Its a very edge case for most people, but seeing as I am working on a searchcode.com upgrade I need a fast highlighter. Currently I am spawning a background Go process using Chroma to take care of this for me, but if there was a decent Java library I would use it. None exist I am aware of that are even slightly up-to date.
gaming industry is huge, but you can't with java because autoboxing everywhere, no value type, and no specialized generics
same for serverless, you can't because you have to ship heavy runtime
same for machinelearning, you can't do math because lack of value type
etc etc etc
Java tried to fix all problems, but they forgot the essential stuff, performance and efficiency
Outside Android, we can enjoy modern Java, which makes Kotlin just yet another language capable of targeting the JVM.
Pre Java 8 is just fine, I don't think it's materially changed, I think what has changed is perception i.e. the realization that all that Enterprisy cruft 'does not equal' Java - nor do wee need FactoryFactor patterns.
My wishlist: - Stream syntax was nice, but a little clumsy - In addition to data objects, some kind of 'un-typed' object (better than a Map) for dealing with json-ish type data, right in the syntax. - First class functions instead of lambdas - Async notation as a better way to express threads, or even combining within the same thread - Union types
For compilation, you should check out Graalvm, which has some neat new features there.
CompletableFutures?
You don't deal directly with threads in java (unless you want to) these days.
- The Java language
- The Java API (and/or whatever set of libraries you're using)
- The Java ecosystem, tooling, etc.
- The sorts of work typically done these days with Java
It's perfectly possible for one person to feel strongly one way about some of those aspects while feeling strongly the other way about others.
For instance, I'm quite fond of the Java language, I mildly dislike the Java API (in how bloated it's become), I'm ambivalent about the ecosystem, and I do my best to avoid the "Enterprise Software" CRUD-churn that Java development often entails (although this might have changed, it's been a while since I did Java commercially.)
I did some Java in the late 90s at university, and a tiny bit in about 2001, pre-dating a lot of the enterprise stuff.
I've come back to it in the last three or four years and it's currently my main language. I think I manage to miss the enterprisey stuff. I remember J2EE happening to other people, and then for a while everything was Spring. Somehwere in there were 'Beans'
Now ... I was pleasantly surprised to come back to it. There are a lot of building blocks and interfaces to use in the various maven-centric public repositories, development is rapid, expressive (I <3 lambdas, which I never thought I'd say) and ... yeah. I'm having fun.
Part of this might be because I did (mostly) C for a decade!
Go and Rust are definitely on the list of things I want to learn though.
It seems like the Java community has lost the art of coding without a framework. It feels incredibly bloated and entirely too magical. But this has more to do with the ecosystem and philosophies of the community, rather than the language, or even runtime, itself. From a purely language and runtime perspective, I think it’s fine.
But I don’t enjoy it. Every time I need to work on a Java project, it’s like it’s a form of punishment. I’d rather do basically anything else.
Punishment for me is PL/SQL which, despite its charming Pascal-like syntax, is painful to work with due to the lack of good tooling and it's awkward constructs (string handling, exceptions, etc.)
Honestly, despite its popularity, I feel the same way about Python. Working in Python on anything larger than simple scripts is difficult due to the packaging mess, the 2/3 issue, it's sometimes unintuitive idioms (think "".join(...)). Ruby is probably the most pleasant ecosystem I've worked with but I find dynamic typing a challenge on large projects. Point being, if I were starting a project in a corporate environment, Java (or at least the JVM) would be my default choice. Java seems to occupy a space where it's good enough or "least offensive" to the majority of the team.
For example, even though passing a function to an SQS listener is many abstractions away from handling internet traffic from SQS, I'd much rather the parsing of bytes be hidden in a place where I never have to see it and I can focus on business logic that actually provides value.
The enterprise-y types are no longer dominant in the space.
[1]https://insights.stackoverflow.com/survey/2019#most-popular-...
It's a lot better nowadays, but historically the biggest problem with Java has been the 3rd party libraries. They tended to be written with extensibility in mind at the expense of usability. Extensibility is great, but it's not the only important thing in a design.
I'm not a huge fan of annotations. It makes the language feel a bit magical and hard to debug in some places. It can be difficult to track down how a given library actually treats an annotation - or even a set of annotations since you can put more than one on a given thing. It puts the language in a strange place: sometimes both boilerplate-heavy and magical.
> The introduction of annotations in JDK 5 has made enterprise Java development a whole lot simpler by allowing things like dependency injection.
This is a bit weird - you don't need annotations to do dependency injection. And you don't need a DI framework either. One thing I don't quite understand is the infatuation with DI frameworks in Javaland. I prefer to just do it by hand where both necessary and plausible.
I have had Code Reviews where "This is all good, but you should use Guice to mock X" where X is the one time I might have a package-private test only constructor with a NullObject for the one dependency I need to mock out in a commit and Guice is not currently used in the package at all.
Personally I think DI has reached cargo cult status in Javaland.
The only downside is that if you parallel test you need to run them in separate processes (which you usually want to do.any case).
Optional is your (and my) friend. It has a small performance penalty and for historic reasons it will never be used everywhere, but for new externally visible code (code not in the class) I try to use it and it makes for face nicer APIs.
I never understood why XML config everywhere and "we should configure it all instead of programming it" took off. You shouldn't change things on production anyway, so the main aspect "you can change it without recompile" is moot and instead you have all the downsides of not having part of your code checked by the compiler.
Some enterprises have managed to move away from this though -- DevOps in the enterprise is real and it looks pretty similar to DevOps elsewhere.
I've worked with teams in very conservative enterprises that have automated tests and CD pipelines, and release 100s of times a day, and support their own code. And with their newfound autonomy -- no more diktats about "use J2EE" from up top -- these developers did what smart developers do: simplify and reduce dependencies and tech debt. Lots of vanilla JS (or maybe React at most), plain old Java, even Kotlin and Clojure where possible (microservices made polyglot development really easy).
I've always thought this was a big reason Oracle dropped J2EE. Their most conservative customers weren't interested as J2EE had become irrelevant for their needs.
Here's a public video of how one institution (JP Morgan) managed to modernize[2].
[1] https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
A lot of this is the language's fault though. For the sake of "simplicity" Java decided to:
1. Have a bifurcated type system with boxing, so a lot of generic code had to be obtuse or use a lot of reflection (It took a long time before Java added a very flawed generics implementation and convenience functions like Strings.valueOf()). 2. Disallow standalone functions (everything has to be a method on a class). 3. Implement many standard library APIs as abstract classes (and worse - concrete non-final classes) instead of interfaces, encouraging developers to overuse inheritance where composition should suffice. 4. Not provide any mechanism for delegating to member objects. 5. Encourage the practice of getters and setters, but provide no mechanism for properties. 6. Provide neither powerful meta-programming mechanism nor a strong type system, but create a powerful reflection API instead. Any surprise it will be misused?
One nice thing about Java (compared to C and python at least) is that you have quite a bit more tools to refactor and despegettify codebases incrementally.
Although Shenandoah looks promising but it's pretty telling that even in 2019 the JVM only has a single good garbage collector and it's still experimental. 23 years of history my ass. Other than the awful JEE ecosystem, the JVM is probably the worst part about the Java ecosystem. You can't fix it with a language that runs on top of it. It's slow (mostly startup performance), the GC is awful and it gobbles up a huge amount of memory. Obviously my company doesn't care because 8GB of RAM for a webserver costs them almost nothing.
It wouldn't surprise me if a WebASM implementation will take over one day, because the JVM is such a failure.
If you don't want memory usage to go up to 400MB, then you can set the maximum memory the JVM can use to less than 400MB. It only makes sense from an efficiency and throughput standpoint to use as much memory available for performance.
JVMs regularly run on GBs or even TBs of heap space in production.
Both Shenandoah and ZGC target lower pause times (at the expense of throughput, naturally). There are commercial offerings like offerings from Azul that target low pause times as well.
> the JVM is probably the worst part about the Java ecosystem
On the contrary, the amount of optimizations that the JVM performs, as well as the monitoring, management, hotswapping, etc capabilities are second to none.
> It's slow
The fact that the JVM tops several charts here says otherwise: https://www.techempower.com/benchmarks/#section=data-r17&hw=...
> (mostly startup performance)
This is already being resolved through GraalVM. See for example https://quarkus.io/
> because the JVM is such a failure.
It only runs the backends of companies like Amazon, Google, FB, EA, LinkedIn, Ebay, Apple, Netflix, and countless others. Not to mention it's being used in high performance spaces like HFT.
> JVM is such a failure
I'm genuinely confused why you would consider that to be the case. Especially since your preferred alternative (WebASM) is just about as bad or worse by your stated measures (start up time, GC).
JVM is really good tech, it allows ecosystems like Scala, Kotlin, Clojure etc to exist. Since Java 8 there is class data sharing that may reduce your startup time and memory requirements a lot, especialy if you run multiple instances with same classes on same server.
Java ahead of time compilation was experimental since Java 9 and GraalVM should also help a lot if startup time is critical.
“I’m going to build a solution that runs on the JVM”
I really liked Java back then because C++ had always been a struggle, not because of pointers or anything like that... but because I always found project management, figuring out how to link libraries, and build my projects, to be overly complicated. With Java, I only had to import a library, and that was it. I didn't need to link external libraries because Java's standard library was large enough that it had most of what I needed at the time, and adding external libraries wasn't nearly as difficult as C++.
However, over the years, I feel like I've kind of grown apart from it and have started to want to migrate back towards C++ and Python. I still use Java because it's the language I know best and it's the language I'm writing my game in, but after I finish my game, I think I'll be using it significantly less. The bulkiness of the code, the complications from the JVM, and the lack of good options for deploying executable code is just getting more and more frustrating.
Also, as I was working on my Bachelor's Degree, filling in the gaps of knowledge left by a "meh" education at my previous school, I could see that Java is just not good for writing good, fast code.
That said, Java 8 did make me like Java a lot more when it came out, but they're starting to deploy new versions so frequently now I feel like 8 is the last version I will bother learning.
GraalVM is a way to resolve that: https://quarkus.io/, https://micronaut.io/
You might want to check out C# as well. .NET Core is coming along nicely, and you get to work in a safe environment, with the ability to drop down to pointers if needed, unlike the unsafety that is inherent to C++.
There is also a C# to C++ compiler that the Unity people wrote, worth checking out.
I wonder what can be considered a white collar programming language which is actually used a lot. Erlang? ES6 with ever changing front end frameworks?
(I am senior dev, some past java7/8 core server experience, FE will probably be React and then mobile)
But once gradle came into picture along with IntelliJ I totally moved to Java.
Why, why, why did they not make JavaFX on Android a first class citizen?
Oracle has made JavaFX open source and available to everyone that wishes to work on it.
https://openjdk.java.net/projects/openjfx/
https://gluonhq.com/products/mobile/javafxports/
Naturally Google prefers to push Android J++.
Good design and java rarely go hand in hand. These small details matter. I don’t think it’s a mistake that Ruby, JS, Rust, Elixir, etc attract people with good design sensibilities which is reflected in their blog posts, websites, and also API designs, documentation, etc.
But otherwise this articles content is not bad, I think we all know why Java and C++ are so popular. It’s one of the first questions every programmer learns their first year programming. This article is a good summary of why. The fact both C++ and Java got things like Lambdas and other modern stuff in recent years is great, but they are obviously incredibly late to the game. Which makes you wonder how long until the other good things will take. Maybe that whole backwards compatibility with decades of some questionable code being a dead weight dragging down the language is one of the reasons why people hate it? You can repaint a broken down house but that doesn’t mean it’s as good as the new house next door.
Look at other examples, CSS - full of bad examples on how do do X, JS - a lot of bad parts, missing core stuff like importing modules , Python - it had is issue caused by some old decisions that caused a lot of pain when migrating version 3.
My point a 5 old years new language will eventually have similar issue in 10-15 years
Then come back as a consultant in 20 years at the intersection of high pay/no one else can figure out how to fix the software.
If you're working with Java you're mostly in medium pay/tearing hair out coz of legacy issues-which you WILL have because a Java shop never COMPLETELY moves to the new thing. Not a fun place to be.
I am working in the present on Web tech, SPAs, but you know what, it sucks even if is the cool thin, the project was started with angular1 which is no longer the cool thing, it uses ES5 and gulp(no longer the cool thing), the backend uses Silex(a PHP framework that is not that cool) ...
The only cool thing I assume is to start new project in the this month cool shit and then after 1 year move to the new shiny and let people like me maintain your inexperienced code in the experimental shiny thing of last years.
I learned vi about 30 years ago, Java and SQL over 20 years ago, and they're tools I can still use every day to get stuff done.
It shouldn't be possible for a new hire to feed a Customer to a Product repository for example, and in most mature languages we've made language level features to doo that, so we don't have to manually code checks in everywhere.
Eventually NewLanguage shows up without type saftey and everything is going great as it gets used in smaller projects. But then big teams start picking it up and suddenly these things are issues again.
I see it all the time and I always found this sentiment incredibly bizarre. It seemed to presuppose that there is language ranking that is just a matter of feature check boxes, or that the introduction of a feature is some sort of admission that it should have been there all along. I hear it leveled at Go in particular but Java as well.
I can't speak for C++, but at least in terms of the Java-the-language most of us would rather see a language feature absent rather than done poorly.
It's not just a matter of borrowing from other languages. Each language's features have particular interactions with one another.
There are a thousand ways that Java's lambdas or var could have been introduced poorly. Go has shown that there's definitely an audience for more austerity in language design.
These feel incredibly bolted on. Instead of a utilitarian, if verbose, lowest common denominator type of language, Java is becoming a Frankenstein monster. And a verbose one at that.
That's an interesting claim, but now that I think about it, I wholeheartedly agree. The reason for that might be Java's poor support for UI programming (especially on the web) and the coders therefore creating their blogs with alternative languages and ecosystems that they are unfamiliar with.
so yeah, evolving in an empty ocean