On a tangent from the article which discusses the Java the languae. I write SQL for a living and the one thing I like about Java based SQL clients like DBeaver [0], SQL Workbench/J[1] and SQuirreL SQL [2] is that they tend to support most databases that have a JDBC driver. Turns out most databases do have a JDBC driver. So if you want to connect to "new" database like DuckDb to Oracle, SQL Server and even CSV files using one client JDBC client is one way to do it. The steps to connect to a JDBC database tend to be very simple and the same for all databases. Get the driver jar file, put it in the classpath and then use the documented connection string to connect to the database.
If you only work with SQL Server then it is probably best you use SQL Server Management Studio. If you use different databases you will need a different client and I find with JDBC based clients you can use one client for to connect to many databases. I have seen many here complain that the Java tools are slow and bloated and look ancient. From a looks perspective you can use FlatLaf themes which look good to me. There are some nice dark themes to chose from.
It's interesting what "popular" can mean. Chances are the "popular" kids in your high school weren't necessarily well liked, they just had social clout.
Where I work now the the "Java" devs code about 80% Kotlin and 20% Java.
At my previous job we were mostly Ruby except for a service that relied on a Java library that connected to some obscure systems that no other language had a library for. Yeah, we used Java, but it was because that was our only option.
Yup, I work at Amazon and my team along with many surrounding teams are only coding in Kotlin from this point on. The JVM and its ecosystem brings insane value to the table, and Kotlin lets you take all the benefits while removing most of the downsides of Java.
why is this ? i have worked on kotlin in the android ecosystem. But something like kotlin with spring is no better than java right ?
you still have to deal with all the controllers, etc.
on reddit, one of the arguments was interesting - the biggest value of kotlin in backend is using var/val instead of types and making everything public (instead of getters/setters).
* Operator overloading is a nonissue. I did like how Ceylon handled it (operators are aliases for normal spelled-out methods) but honestly overloading is not something I ever miss. And it was a minefield back in my C++ days.
* Dependency management in Java is decent. It's worse in every other ecosystem I've encountered.
Stuff he gets wrong:
* Checked exceptions are horrid. The people who hate them aren't driven by laziness, they're driven by the desire to use standard interfaces - especially now that we have lambdas. You can use `Runnable`! Unless your code throws a checked exception... in which case you have to go through the ceremony of wrapping it. This is stupid. You can have standard interfaces, or you can have checked exceptions, and standard interfaces are vastly more useful.
Most frameworks (and not languages) use everything public. Rails, React, golang's frameworks, flask/fastapi. Code has generally not suffered from being public.
Would you not accept a Java framework with everything public ? i asked this question on /r/java some time back and got badly flamed. So im trying to understand this. This seems to very philosophical...but then most other frameworks in other languages dont carry this philosophy.
I'm currently working in a Java codebase without Lombok and the habit is to make all fields public for record-like objects. It's fine 99% of the time.
Every so often I want to change the behavior of something, and it's a big PITA. For example, maybe I'd like to ensure there's a default value for something that is sometimes null. If access was via getter method, I can just override the getter:
class Foo {
private final String name;
public String getName() { return name == null ? "Default" : name; }
}
No need to change the hundreds of places that call .name or .getName().
This kind of thing happens often enough that it really should be baked into the language. Lo and behold, most post-Java languages support it, including C# and (modern) Javascript. Java is a bit stubborn in this regard, but Lombok solves the problem in a way that is almost language-native.
>I'm currently working in a Java codebase without Lombok and the habit is to make all fields public for record-like objects. It's fine 99% of the time.
this is so cool! thanks for sharing that anecdote. trust me, ur the first person who has ever written this publicly.
so I hear your argument for getters/setters. And IMHO, all paradigms do have their usecases -probably so do CQRS and everything else.
But ultimately, you can refactor using the tools available today. I would (politely) argue that your example is also a notnull situation and perhaps i would have solved it on initialization, versus the way you did it.
Ultimately, you can create getter/setter structures in python as well. But in most large codebases, it still ends up being unnecessary.
Sometimes. You can refactor your own codebase. If you're working with independent modules, or building libraries that you publish to the world, refactor tools will not help you. You can't refactor other people's code.
Even in your own monolith, this kind of refactor is not great. You've turned a 1-line logic change into a patch that spans potentially hundreds of files. Reviewers hate that.
> a notnull situation and perhaps i would have solved it on initialization
You don't always get to decide that. The data may be serialized in from JSON, loaded from a database, imported from some weird EDI system, etc. Maybe the initialization process is not so simple, and spread out all over the codebase (or even just dozens of constructors).
> you can create getter/setter structures in python
Python is extremely dynamic and while I can't remember how offhand, I would be surprised if you couldn't simply override variable access the way you can with Javascript (eg, add a `get fieldname()` method). So you don't need "getters" the way you do in Java, which makes a distinction between field access and dynamically dispatched functions (and has performance benefits).
>Python is extremely dynamic and while I can't remember how offhand, I would be surprised if you couldn't simply override variable access the way you can with Javascript (eg, add a `get fieldname()` method). So you don't need "getters" the way you do in Java, which makes a distinction between field access and dynamically dispatched functions (and has performance benefits).
So this is the thing - that there is nothing in python at a language level that enables/disables this. it just doesnt give a private construct. same as in javascript. you can add getters/setters if u want. in fact ECMAScript introduced getters/setters - https://www.w3schools.com/Js/js_object_accessors.asp
But the thing is, huge huge codebases exist without private.
There's nothing that i can see in java that requires private. not to say, ur not wrong...but so it is with almost all patterns. even CQRS. There is a certain level of complexity when it makes sense. The question is - how early do u enforce this ?
> that there is nothing in python at a language level that enables/disables this
Yes, there is. `@property` allows you to change a vanilla field to a getter/setter without modifying client code.
>> override variable access the way you can with Javascript (eg, add a `get fieldname()` method)
> in fact ECMAScript introduced getters/setters
That exchange was weird.
> huge huge codebases exist without private
I'm honestly not quite sure where you're going with that. Huge codebases exist in COBOL too.
It sounds like you're trying to argue that the software world doesn't need encapsulation. I disagree. Maybe you took my comment "99% of the time it's fine" the wrong way - 99% isn't good enough. The 1% wastes a lot of time and energy, it's better to have language support for encapuslation.
Java's support for encapsulation (getters) is pretty tedious. Lombok and records (too modern for this codebase, unfortunately) fix that. Thus "getters/setters are a nonissue".
I wrote it, so feel free to consider me biased. That said, "wrote" is a strong word, it just glues some other projects together with Guice. Not much actual code there. Which may be the answer to your question - if you asked me what's wrong with other frameworks, "too much code" is a decent answer.
I have been wondering something - wiring different components together and building "up" something.
versus
take Spring Boot and create a DSL/Lombok-like layer on top which abstracts out the "too much code" and unpleasantness. Like a Nextjs that builds on top of Reactjs.
I'll give you my complaint with Spring: It tries to hide the libraries underneath and offer the "Spring way". But it's always a leaky abstraction, so you end up having to learn both (for example) Hibernate and how Spring interacts with Hibernate. Instead of avoiding a learning curve it just adds a second one.
I greet your suggestion to add another layer of abstraction on top with great skepticism.
Over time I shifted my opinion to consider that ternary operation as "logic", so the Foo is not the right place for that. Either Foo should've been created with "Default" already set to the `name` field, or Foo users should make the decision themselves.
Foo class can only contain something, but not make any decisions, no matter how small, because it's not obvious whether a decision is still small or not anymore.
Getters are still useful though, for example:
class Foo {
// all contents of Foo, but we want to keep it serialized for performance reasons
private ByteBuffer bb;
public Long getId() {
return bb.getLong(ID_OFFSET);
}
}
I'm just going to quote myself from a few hours ago:
> You don't always get to decide that. The data may be serialized in from JSON, loaded from a database, imported from some weird EDI system, etc. Maybe the initialization process is not so simple, and spread out all over the codebase (or even just dozens of constructors).
Real-world codebases (the ones out there running profitable businesses, built by programmers that have come and gone) are rarely so clean and pretty that you can say "well, you just should have initialized it properly in the first place!" To that, I offer you Mike Tyson's incredibly apt programming advice: "Everyone has a plan until they get punched in the mouth".
Records are classes with a canonical constructor that lists every field it has. The fields themselves are not public, but a method with the same name is automatically generated for each (basically a getter, just not getName(), but name() as the former is not consistent (what should be the getter for field Xx?). This result in records being shallowly immutable.
If everything is public, you don't make a difference between an interface and implementation.
Meaning, your framework users can depend on any implementation detail and if you change that detail, the framework will break for these users. They will be disappointed since they didn't know they were doing something wrong (all is public and thus available to use).
thats a good reason, however a lot of code doesnt need to do these kind of contracts. They are perfectly ok being more accessible to developers than enforce a lot of correctness.
Which is where a lot of the JS/Ruby/Python frameworks operate. You're not wrong - my point is that a lot of places, it is not necessary.
> Code has generally not suffered from being public.
I spent 10 years suffering under a ruby app where literally every interface had to be considered public because someone might have gone code surfing and decided to be clever, so literally nothing was a simple code refactoring of private interfaces that nobody should touch.
I don't understand what kinds of software people who make this argument write, but I assume it has to be something like web services where the http routing layer is the actual public interface definition and so you're benefiting from strong encapsulation at that layer. Yes, in that case the language doesn't need strong encapsulation because your external framework already provides it for you.
>web services where the http routing layer is the actual public interface definition and so you're benefiting from strong encapsulation at that layer
this is actually one of the best ways anyone has put it. You may be right. I have often wondered how does the huge amount of Ruby/Python/JS work in building large companies and products, when Java mandates all this amount of massive complexity.
You may be right here - in the web services world, the hard contract of the API layer makes up for less strict encapsulation everywhere else.
I think there is a fundamental difference between “OOP classes” and data. Java didn’t have a way to differentiate between the two so a convention was created with POJOs with properties, so reflection-based tools could use arbitrary classes in some way.
Records are imo a really great addition, making the difference explicit.
As for why private fields are important, think of a class implementing a circular buffer with a fixed-size array. Here, the start and end indices used are private state, which is a must for proper OOP — without it it couldn’t enforce class invariants.
not denying "proper OOP". my point is that a lot of code does not need it. If u were writing the exact same code in go/nodejs/python, you would get by fine without it.
You would not break the contract of maliciously trying to access variables that shouldnt be accessed.
For example, a popular convention is to have underscored variable names (__thisisprivate). Works perfectly OK.
99% of getters/setters dont do anything more. They do exactly this. No validation, nothing. So I'm questioning the ordinary....not the special cases.
But at this point with records... I don't care. I declare a Integer 'bla' and a 'getBla()' is made available by the IDE right way.
Sometimes the records fields end-up being accessible without the getter. ( declaring the record in another class for instance...). But that is also a moot point : all the data they are holding is final and I know what can be null or not.
Properties being private seems to give Java devs (and also PHP devs to a lesser extent) a false impression of security. The lack of public/private/protected is always a surprise to fellow programmers when I introduce them to Python, even though we still have conventions and it's never a problem in practice.
I don’t know, it seems to be a significant reason why java codebases, no matter how bad and written by how many almost completely separate teams chug along for decades. Empirical evidence does show that Java is very good on the maintainability front:
I use this argument quite often when someone jokingly snark about me working mostly with Java.
It's not my favourite language, I've worked with quite a few others, Java has just been the most consistently less painful than anything else. Ecosystem is pretty good, dependency management works; refactorings are easy with modern tooling and the typing system also works fine.
Yes, it's verbose as hell and you definitely will depend on some basic tooling to be productive, I've worked in monstrosities that we managed to strip out piecemeal and get the codebase in good shape and modernised, it does require time and an experienced team though.
Like you mention, the systems keep chugging along, in places with good engineering practices it's also a given to keep updating those codebases, simplifying and it happens organically when systems are running for 8+ years with 20-30 different engineers rotating in/out during this time.
It works, it's not beautiful, it's not concise. It has quirks and gotchas as any language, and requires tooling, but it's quite mature and modernised enough to be a safe bet for bigger projects. Better than any alternative I've had experience with at scale.
We've all seen unmaintainable monstruosities in any language. I think maintainability is a subject that goes way beyond language features. But if I were to relate a language feature to maintainability, that would be proper typing, not variable accessibility. Even though it's not enforced at runtime, a Python code base with type hints makes a huge difference in maintainability compared to one without. Of course Java has that by default and it's nice.
Came here to say exactly this. Exceptions are a poor-man's implementation of an error-handling applicative or monad type. Checked exceptions are especially terrible because their usage forces handling of exceptions in every layer of the call stack above the point where the checked exception might be thrown instead of simply writing a single exception handler at an appropriate layer.
> Exceptions are a poor-man's implementation of an error-handling applicative or monad type.
True, but in those languages, exceptions can be thrown from anywhere anyway.
> Checked exceptions are especially terrible because their usage forces handling of exceptions in every layer of the call stack above the point where the checked exception might be thrown
That's not true. Handling a checked exception in one layer means you don't need to handle it in the above layers.
>but honestly overloading is not something I ever miss. And it was a minefield back in my C++ days.
For a lot of things, it's fine. And there certainly are potential problems with operator overloading. But the experience of, say, reading/writing, linear algebra code using a C++ library with arithmetic operations on vector and matrix types vs using its java bindings where that's all done through explicit method calls wildly favors C++.
In fairness, that's more or less what I remember (from 20 years ago). Operator overloading in libraries built by incredibly smart people could produce beautiful syntax. Unfortunately, most library builders weren't that smart. Maybe it's better now.
As a language feature I can't feel more than ambivalent about operator overloading. Kotlin has it, Java doesn't, I'm ok either way.
I think Rust’s approach is a good tradeoff. Don’t allow arbitrary operators like haskell/scala (!!< is never gonna be readable), but do give an Addable/Multipliable/etc interface/trait which will define add/multiply, to which the corresponding symbols desugar to. It might even help heal the object-primitive rift in java.
The JVM is really a marvel of engineering. I have yet to see another system that offers what it does in terms of performance, GC tunability, observability, monitoring, profiling, etc. .NET is the only thing that comes close, but it has a ways to go in terms of what the JVM is capable of.
Java the language is really coming along nicely. With records, pattern matching, string templates, virtual threads, value types (in the work), and more, it's shaping up to be a top notch development language.
The main reason behind Java's success has always been the community and it's ecosystem. Once you learn basics you literally can code very good things with ease using a huge number of examples/solutions present on web.
The user base kind of helps the developers find the fixes way too easily compared to any other language.
I doubt it is ever going to lose it's popularity as Java is very good to accept good changes(although slowly). The best thing about java is it provides code stability and backward compatibility and this is the reason it will thrive even more.
C++ still exists because existing projects were written in it... that's it.
There's no value in using it for future projects other than a wider pool of developers to source from... a network effect that will diminish with time.
Java is objectively dated in so many ways, it's not really defensible to say otherwise. Tons of unnecessarily verbose and boilerplate aspects of the language.
Ignoring network effects and existing libraries, which language feature of C++ makes it better than a Go or Rust for similar use cases? Which language features are more concise and easy to use? What objective traits of the language make it superior?
Ignoring network effects and existing libraries is a good way to make an irrelevant argument. It would be like requiring the Marine Corps to train in BJJ and Kickboxing because that works best in the Octagon. Great, but the Marine Corp doesn't fight in the Octagon.
In general, I'll take a somewhat mediocre tool where I can look up examples/libraries to do everything I need, with a large pool of competent developers to hire from, over an excellent tool that may or may not do everything I need at the level that I need, that relatively few people know how to use.
Of course, standards can change. But Go and Rust have a long way to go to build the network effects/libaries/ecosystem equivalent to that enjoyed by c++. I'll let the enthusiasts figure that out while I build things that get deployed, I look forward to the day it's ready, lot of promising features in those languages. Thanks in advance!
Yes, so you're talking about network effect momentum.
In 20 years Java will not be the default choice for the vast majority of projects, just as C isn't now, despite being so in the 80s... it's obvious from the verbosity of the language. Network effect will fade over time.
Kotlin is already a drop in replacement for Java that is less verbose, more ergonomic and still compatible with the existing ecosystem. Why would anyone use Java over Kotlin on a greenfield project aside from prior familiarity? They wouldn't
Right, and if that momentum is going to last 20 years, then that's going to dictate what's a good strategic decision and what isn't for the next 20 years. Thanks for letting me know what the good options might be when my infant daughter is halfway through college, that doesn't help me save for it in the meantime.
Prior familiarity is a very good reason to pick an option for a greenfield project if you're operating on any type of serious budget (time or money), especially if you need to hire others to help out. There's also the annoying reality that most libraries for new/up-and-coming languages are simply inadequate, despite whatever claims they make.
For instance, one of my personal side projects involves getting familiar with WebAssembly (note: not on a serious budget), and I'm using emscripten to transcode because that's what the internet seemed to think was the closest thing to a standard toolkit. I found a bug simply by combining two pieces of example code from Emscripten's own documentation (you'll note I'm transcoding from c++ due to prior familiarity): https://github.com/emscripten-core/emscripten/issues/17143
This is not a dig on emscripten, merely pointing out that it's probably not mature enough for the stress of corporate-scale development, where you encounter all sorts of crazy edge cases well beyond the sample code. Java over the years has obtained that level of maturity. The edge cases are largely solved or at least known, and there's an army of experts and consultants ready to help if there's a problem. When time is money, that matters. It determines the risk profile of any project, greenfield or otherwise.
Java's momentum hasn't stopped, at best it's simply slowing. And at this rate it'll take decades to come to a stop, and decades more to recede to any meaningful degree. I'll also point out that C is very much alive and well in the embedded world. Plenty of job postings looking for C experience explicitly, for new products as well as legacy.
aka Ignoring the fact that it's easy to hire developers that already have widespread experience in C++, and ignoring the cumulative effort that thousands, if not millions of developers have put into writing and testing code that I don't have to... wait, why would I ignore both of those things?
If I'm creating software in a vacuum, such that I don't have to think about things like developer experience or time-to-market, then sure, let's say that Go or Rust is definitely better for many use cases. Very few people create software in a vacuum!
You started with "C++ getting replaced by go and rust everywhere" (an enormous exaggeration), and now you want to argue about languages features. OK.
C++ is in a state where it's good enough and slowly getting better. The enormous ecosystem, from libraries to game engines like Unreal, means it's not going anywhere for decades. New "greenfield" games are being written in C++ every day.
> C++ still exists because existing projects were written in it... that's it.
False.
> There's no value in using it for future projects other than a wider pool of developers to source from
Well, you know, that is value. And there's value in being in the same language as the libraries you use. And there's value in C++ in the embedded space. And there's value to being able to control memory alignment in the high performance space. And...
If you're writing some generic web service, sure, there's no reason to write it in C++. Your statement was far too broad, though.
> C++ still exists because existing projects were written in it... that's it.
Obviously wrong, but sure, go on.
> Java is objectively dated in so many ways, it's not really defensible to say otherwise. Tons of unnecessarily verbose and boilerplate aspects of the language.
What Java are you talking about? It might have been true in the past, but modern versions of Java are just normal Algols. It's on par with C++, C# and Go, the other mainstream options you'd have for a project you'd write in Java.
Sure, it's not as terse as Python, but neither is C++ or C# or Go.
>> C++ still exists because existing projects were written in it... that's it.
That was the claim. Between "Rust exists" and "the only reason C++ still exists is existing projects were written in it" there's still a fair amount of ground for you to cover.
You said that Rust is more memory safe. Hard to argue. And more concise, about which I have no information. Those are good things. But, as I brought up elsewhere in this thread:
Is Rust as easy to use in the embedded space? Is it available on as wide a variety of chips?
Does Rust make it as easy to control memory alignment, for those who need it?
Does Rust make it as easy to interface with libraries, many of which are written in C++?
Memory safety is good. Conciseness is good. Those are not the only reasons one might choose a language.
Rust absolutely lets you control alignment, and use SIMD. I won’t claim to make a comparison to C++ here because I have never done that sort of work in it, but it is absolutely a thing.
There are tons of modern languages that allow low level memory access, including Rust. Nobody chooses C++ due to it being more convenient than modern languages.
I can't really see Java's time being gone for large, enterprise-level fintech/banking. Payments, integration with other large-scale systems, etc. Multiple payments or finance departments in FAANGs are still running Java, for example.
You need to separate the language from the ecosystem. Yes, network effects will cause many languages to drag on in use, but that doesn't mean they're going to live in perpetuity.
COBOL was used for a long time for various business backends... did that mean it was a great language that was going to be used in perpetuity? no. And I assume the majority of those don't exist anymore, despite existing as recently as the 2010s
I just think it's weird to say Java's time is gone when Java is still the language du jour in a major employment field of programming. Maybe Java's time is waning but I wouldn't call it gone. New payments/fintech startups and greenfield projects are still choosing Java. I don't know a significant field whose new companies are still going with COBOL from greenfield.
Additionally, what is replacing Java in the finance space? Genuinely asking, because I have no idea, and can you point me to some companies using the stuff that's replacing Java for banking/finance/etc?
Yes, there was a time that C was used for pretty much every app. That time has come and gone as higher level languages became more popular.
So too will Java's. The language itself is objectively more verbose than many modern equivalents. Network effects and existing libraries aren't facets of the language and over time the gap will narrow
That assumes that that minuscule time being spent on writing 3 more characters+ is actually a productivity hit (it is not).
+ I am actually not sure whether even that is true. With how great intellij is, I would honestly wouldn’t be surprised if writing a java program would come out as less keystrokes than the equivalent program in a less verbose language.
Can you please educate me on what are the languages and frameworks that enterprise-level finance and banking are going into in replace of Java? I'm good-faith asking.
Additionally, "the gap will narrow" is not the same as "Java's time is over", which is the only thing I'm having an issue with. Over implies that the gap is already closed, but I don't think it is, and if I'm wrong I sincerely want to know about what the new language du jour of a major employment field is.
C never had the insane hype that Java once did - at the height of its popularity it had an insane amount of mindshare, and probably no language will achieve such ever again. It was literally the only PL that was/is known by laymen.
If anything, the field will fragment a bit more, but java likely never gonna die — cobol and fortran often run in VMs due to the hardware not being as long-lived as them, due to the JVM model, java is not even weak to that.
Don't forget that new release of Java is coming out every 6 months and that (almost) every one of them brings some core language improvement aimed at reducing verbosity. New long-term release is out every 2 years. How many C releases were there in the last 10 years?
C++ is definitely not replaced by Go, nor by Rust.
C++ as well, but especially Java likely has multiple times as much projects than all the smaller languages summed. They are ain’t going anywhere anytime soonz
> Checked exceptions are optional. But they're one of the BEST features in Java.
No
> So much code fails unexpectedly. When you want to build a professional application you need to handle every error.
Yes
> Checked exceptions help you avoid that nonsense.
Wrong
> People hate checked exceptions because of laziness. Java guards you against yourself.
Wrong
Java checked exceptions are almost universally regarded as being a mistake because they don't actually solve the problem they were meant to solve, and they make the code extremely hard to work with.
These are not the reasons I like Java at all (I still use Java 8). My reasons are:
1. C-like language - Flip between C, C++, Java and JS with minimal thinking about syntax.
2. Build once - I can build locally and deploy the byte code straight to production. I don't need to worry about the production server having the right packages as long as everything is bundled in my JAR.
3. Libraries just work (mostly) - It doesn't matter if the library was written yesterday or 10 years ago, as long as it was written sanely, it should just work.
4. Garbage collector - Really in top class. I can create and throw away huge amounts of RAM and not have to worry about leaks and have it dealt with realitively little performance impact.
5. Memory safety - Never having to worry about pointers, segmentation faults, etc.
6. Exception handling - And if all else fails, Java will ultimately give you the opportunity to recover from many different scenarios. This may seem like lazy programming, but actually it's for the edge cases you can't think of. If your program does happen to crash, you get a very good stack trace.
Java may be cumbersome and repetitive is some areas (and perhaps the newer additions to the language deal with this), but I would take them every day of the week.
The only thing that has really left me wanting is C-like performance and networking capabilities. Most other things I want to use are very well implemented.
Started my career with Java and I will never work with it again. It is mostly the culture around Java. Most Java code is verbose to an extreme but the devs themself will say it makes the code readable. They will say the language is functional because of lambdas but anyone that has used a functional programming language will know that it feels nothing like it, again because of its verbosity. It is so complex that you can't really work with it without having to use an IDE.
I don't get the "don't need to use an IDE" excuse. Almost any present day project is big enough that you need an IDE, regardless of how simple a language is. This is even more so for dynamic languages. Who doesn't want immediate type checking, safe and effective refactoring, call stack lookup, and the many other features an IDE provides? Do people making that claim write code in notepad or something?
Modern Java isn't really any more verbose than other languages it gets compared against. It is very nicely balanced between expressiveness and readability.
No. The choice isn't limited to an IDE or Notepad. There's also the choice of a reasonably powerful text editor like Emacs, Vim, or VS Code. Those have many of the advantages of an IDE while still being more flexible and not trapping you into an IDE platform that is often hard to move way from (generally by intention by the IDE creators who want lock-in).
The poster was saying that Java is so complex that you need an IDE. People use VS Code to write Java today, does that mean that they're not using an IDE? As long as the same features are offered in VS Code and IntelliJ/Eclipse/NetBeans/etc., then the distinction is really nothing worth talking about.
Then Emacs is an IDE and Vim is an IDE. VS Code is just a text editor that has extensions for specific programming languages, just like Emacs and Vim. The confusion is just because it happens to be named after an actual IDE Visual Studio, which it has little to do with just for marketing purposes (like how JavaScript has little to do with Java).
> VS Code is just a text editor that has extensions for specific programming languages
Extensions that offer syntax highlighting, debugging, refactoring, code inspection, command line integration, dependency management, etc. How is that different from an IDE?
All those can be done in Emacs and Vim though. VS Code is only surprising to people who haven't used serious text editors, which have had these features for decades. But what Emacs, Vim, and VS Code don't have that IDEs do is the idea of proprietary "projects" for building your code that don't work if you don't use the IDE. Instead you are free to use whatever system you want. Makefiles. Cmake. Ant. Maven., etc. Your choice, which you can easily use with any other editor you choose.
> They will say the language is functional because of lambdas but anyone that has used a functional programming language will know that it feels nothing like it
That is such bullshit. There are plenty of very functional libraries written entirely in Java, it has a surprisingly adapt type system that makes plenty of things expressible (not monads themselves, but you can almost get there with purely generics). Vavr, jool, immutables.
And what exactly is complex? Java is on the leaner end of language complexity, just compare its number of keywords to.. any other mainstream language.
How do you suggest getting rid of DAO/DTO? This has nothing to do with Java, but with the domain at hand.
I worked on several golang projects where protobuf was used, and the same dichotomy showed up in the code. There were three types even, protobuf types, internal types, and DB types, with manual mappers and validators having to be written between them. Quite a bad, verbose, and error prone experience, especially that golang itself is quite verbose.
here's my thought - and ill be the first to admit that i may not be correct.
its the front loading of a huge number of patterns which ends up being the barrier. An argument is made for "future proofing". Its not unlike Python whitespace indentation or golang's unused variables restriction.
But the design pattern complexity is far more harder than all these things.
There is a certain degree of complexity when all of this starts making sense. Or maybe do what Redux did ... and abstract everything out to somewhere else.
I couldn't disagree more with the point about checked exceptions. Pretty much everyone else has essentially agreed that checked exceptions were a mistake. They leak implementation details and/or pollute your interface. Practically speaking, they don't force you to deal with exceptions. They force you to catch them. Anyone who has done Java in the real world has encountered:
try {
// do stuff
} catch (SomeException e) {
// do nothing, maybe log it
}
I strongly agree the verbosity is a non-issue. Any modern IDE just fills this in for you. Yes more verbose syntax for getters/setters would be nice but it's not a big deal.
But there's one point I wanted to highlight. Every language that comes after Java should've learned Java's lessons and some didn't.
C# quite famously did. C# really is Java 2.0. No checked exceptions, reified generics (rather than type erasure) and some cool stuff too (eg LINQ, partial classes).
Other languages (eg Go, Rust) largely moved away from exceptions entirely. Personally I agree with this. Exceptions are a false economy that devolve into control flow that's hard to follow (eg Java has a parse number from string that throws a ParseException if the parsing fails and that's just not an exception).
But the big one is dependencies. Every external dependency system that came after Mavaen should be at least as good and many aren't. I'm looking at you, Go. This was a shocking omission for something aimed at being a better systems programming language (which it never would be as a GC language but that's another story).
But Java is proven, mature and sufficient. That's why it's still used.
Go's module and vendoring system sure was kind of a rocky transition. It left a lot of older projects in the lurch in terms of adaptability without having to take extra time for reworking a few things, especially if they used some third party system like dep or whatever and just never got updated after, which kind of crunched against the design goals of forward compatibility. I think they just kind of aimed to keep it as simple as possible in line with other design goals of the language and tooling, which is fine for its use case. It would have helped a lot if they had done this from the start, though.
I don't really think that Go was intended to be a systems level programming language, I've never used it that way personally. I think of it as specialized towards being an excellent applications level programming language with rich-enough system level interfaces where necessary. People who do stuff like programming a bootable OS in go aren't doing it because they intend to develop it into something everyone will use, it's done more like an exercise.
Rusts error handling is almost identical to checked exceptions in practice. The reason it works is because there is no real equivalent to easily handle-able unchecked exceptions, and because macros and sum types make it very easy to wrap a lower level error in your higher level error and rethrow.
Basically, checked exceptions could have worked, if they were the only type, and had better ergonomics of use, and a language culture that emphasized correctness more.
Checked exceptions were wrong for Java, but that does not mean they are wrong universally.
Yah, I've always felt that the "checked" should not have been part of the exception type, rather it should have been part of the throw:
throw checked new Exception();
Then the caller knows there is a special reason to handle this exception, and it's also easy to write a tiny wrapper to neutralize the check, if needed.
For example: failure to open a file could be nothing major in one case (just return null), needs to be handled in another, or should cause the entire application to exit. This depends on what file was opened - but it does NOT depend on the type of error.
Checked exceptions are an exact analog for result types. If you have an `int parseInt(String s) throws ParseException` function it is basically equivalent to `parseInt(s: String): Result<Integer, ParseError>`. Hell, if anything, it is better — it is automatically “unboxed” for you, and in case of an error you get the sane default choice of bubbling said exception up (rust’s ? macro does the same, but it is much less ergonomic). It has the added benefit of including stack traces, which makes it actually possible to do anything with unhandled error cases and is of utmost importance.
I’m not claiming that Java’s execution of checked exceptions is good, it unfortunately does have a few inconsistencies (lambdas don’t operate well with them, also inheritance is not a good match for exceptions), but it was pretty universally ditched and I think it very well deserves another chance.
My issue with checked exceptions in Java is that it becomes another branch of control flow. It increases cognitive load to keep in mind that exceptional cases are basically running a `GOTO` that you have to keep track of.
I think they can have its place but operating well with lambdas would be a must... If Java supported a multi-value return, like Go and Python do, and control flow was based on `var a, err = obj.exec()` or some other syntax that could work nicely with Streams and the functional features of Java I wouldn't have an issue with checked exceptions. A multi-return for errors with checked exceptions would even allow the compiler to narrow down all the possible `err` types you could get, automatically generate a Java 17 `switch()` to parse through all the possible cases of checked exceptions. It could help a lot with error handling.
> Java has been around for nearly 30 years and is still mostly compatible to Java 1.0. That is fantastic and unrivaled!
I’ve been writing java code since about 2001 and up until Java 8 this was fairly true, and definitely a fantastic thing.
However the leap from 8 to 11 or 17 (all LTS releases) is a painful process. We are moving from 8 to 17 at work, but the number of issues we run into with incompatible libraries is a right royal pain. Especially when it’s down to the (imo) silly decision to rename the java EE packages to jakarta. A pointless exercise (imo) that has held back a lot of organisations from upgrading sooner.
> The nice thing is that the JVM is a big tent. If you aren't a fan of Java, Kotlin or Scala might suit your taste.
This is his conclusion, and I, well, agree. While I was well versed in Java, once I started playing around with Kotlin (which was around the time Java was stagnating), I never really looked back.
are u happy with the state of frameworks (like spring, etc). not necessarily the language java or kotlin.
Because coming from the flask, go, jsapi ecosystem...it was very hard for me to be productive in the jvm world (kotlin or java), cos of the frameworks.
I don't care much for Java. However, if you want a performant GCed language with a top tier ecosystem JVM languages must be in contention and there aren't many other viable choices.
WASM might shake this up in a few years. We'll see.
> The fact that operators can't be overloaded is a HUGE benefit.
Not if you're trying to work with other algebraic data types.
> If I see a + b I know that this is either a string or a number not some hidden method.
Great if your world only consists of numbers and strings.
And really in this day and age with strongly typed languages your IDE should be able to easily tell you if someone is doing something totally insane with any given operator. In most cases, they won't be and it'll be easy to decipher exactly what they mean.
Stop trying to prevent everyone from being able to do useful things just because some people write crappy code that abuses language features.
I have a question for those of you who have worked on java..or considered working on java ..and ultimately did golang or something.
what would make you choose java :
1. is it a better framework than spring boot
2. is it a better tooling like heroku or something for java
3. is it better community & programming paradigms ? I once asked a question "can i write a java api using all variables as public" and got flamed pretty badly. Do people want an alternative to the MVC/DAO/DTO structure ...where the community doesnt flame them ?
4. is it something else ?
Because based on docker pulls, java still is probably just as popular as nodejs or something.
I'm less than enthused by the spring ecosystem. It does help you start quicker than you would without it but while running it in production over time I think it has costs I'd rather not pay so I suppose a better framework would excite me too.
Java as a language is fine and I have no real complaints there.
>while running it in production over time I think it has costs I'd rather not pay so I suppose a better framework would excite me too.
can you elaborate on this ? really have been considering spring boot, etc. But coming from the python world ...i feel it cognitively very hard to get through. But im super keen to know what issues come in production.
Most of us were under the impression that Spring in production just works.
>so I suppose a better framework would excite me too
especially from this perspective. What would you want ?
My complaint about spring is the same complaint I have about almost every runtime dependency injection framework. In the happy path everything just works. But when you get off that happy path which happens all the time in production for any system older than a month you get exceptions and those exceptions list stuff in the stack trace that you've never seen and didn't even know were being used. For those issues every once in a while when you try to dig into the error you find yourself deep into runtime reflection code somewhere trying to figure out how this particular configuration happened. How your code got glued together is a mass of difficult to read code buried deep in the spring framework.
We have a saying about these types of frameworks: They are a great way to turn compile errors into runtime errors.
When you are just starting out on a project it's great. You get up and running fast and as long as stuff doesn't change too much you are probably fine. But when your codebase reaches 5+ years of age it's highly likely to see a spike in "runtime errors" that potentially could have been compile time errors every time you release and releases get nerve wracking for a bit until eventually the codebase get's frozen in stone and someone starts suggesting we rewrite it.
I should mention that this isn't the fault of dependency injection itself. It's the natural result of doing that dependency injection at runtime using reflection. A better way is to use code generation so that you can actually see and reason about the code when something goes wrong.
I don't choose Java but I work with it and I mind it less and less.
1. is it a better framework than spring boot
Dropwizard has a refreshing approach.
2. is it a better tooling like heroku or something for java
Working with kubernetes is seamless. Lots of ready made hooks to your JVM and your application
3. is it better community & programming paradigms ? I once asked a question "can i write a java api using all variables as public" and got flamed pretty badly. Do people want an alternative to the MVC/DAO/DTO structure ...where the community doesnt flame them ?
There is a strong push toward immutable everything and final all variables. It's ugly like often with java, but it's attainable by settings the linter to the max and adding yet another annotation ( to the package folder itself, and it's not enforced by the compiler unless you ask for it... )
4. is it something else ?
At this point I can crank out things fast in functional & immutable manner. Tests write themselves. JVM is rock solid.
It’s as sexy as it gets, even as it is. It has one of the biggest ecosystems, has insanely good performance, the best GCs, very good debugging capabilities, best dev tools, and that’s just the present.
It already has loom in preview which will make the whole previous thing suddenly non-blocking (this is uniquely possible with java due to the ecosystem being 99.9+% purely java, no FFI).
Once value types arrive, I will honestly see no niche other than embedded/lower-level not served by this platform. Oh, and there is of course Graal which will bring all these capabilities to even polyglot code bases, you can just execute some python script on basically java objects with java’s speed!
I would prefer better integration of modern java features into frameworks. Pretty much ditch data POJOs for records, things like that. It is a bit harder to do for JPA entries as they are not just data, so they would likely need a wrapper, but the result of an sql query is a “record”, literally.
We (a team that has extensively used JVM before) went with Go primarily for the GC characteristics and WASM support. GraalVM was promising but seemed like it had a way to go when we prototyped on it.
The GCs on the JVM are superior to what golang offers. You have several to choose from based on the use case you have (batch processing, web backend, latency sensitive ,etc.).
In my experience, you only need to start looking at GC parameters once you hit a certain scale. For the majority of services you'll be writing, especially in a small team, you don't have to worry about them. G1GC is really good, and if you need low latency, you have ZGC and Shenandoah.
We've seen the hoops that people had to jump through to get golang GC to work properly once they hit a certain scale.
The main reason we chose Go was compilation speed since our product would need to quickly compile and generate something that is fast to startup, and Graal was just too slow when we prototyped things.
The GC benchmarks we did showed that Go would work as well, so we begrudgingly moved to it.
IMHO the problem with Java/Springboot compared to a Go or Ruby/Rails or JS/Node etc is the paradox of choice. For 99% of applications it doesn't really matter DB or html templating or background jobs or whatever you use. You want to use whatever everyone else is using for long term support and ease of hiring/onboarding.
Maven is a pretty horrible dependency manager. I would take NPM over it any day, because it is so much simpler. There is much less that can go wrong, while with maven you're fighting with the tooling all the time. pom.xml files are much larger than package.json files. While I can read and understand a package.json no problemo in a couple of seconds, with pom.xml files I'm completely lost for hours on end, even when I wrote them myself a while ago.
I'd say Maven is more than a dependency manager, though. It's a full build manager, with dependency management as one (major) feature. The dependency management part itself isn't very complicated, other than the verbosity of XML as the structure.
I have spent a long time as a Java dev using Maven, and the past few years floating around some other projects (C#, PHP, Python, a tiny bit of NodeJS) and continually find myself wishing to have Maven back, so I suppose YMMV.
Ironically though, every time I start a new project with maven, I end up needing to google how to actually build a runnable thing. I end up needing to configure some kind of maven plugin to build a fat jar.
Why did I include this package B? Oh it addresses a security concern from a dependency of this other package A. Hope you put a comment there. Wait, there's a security issue with B also? Add another package C to deal with the security issue in B. That's just 1 aspect.
Now let's munge in plugins and plugins of packages and it gets messy real fast. Outside of the outdated XML, is there a way to deal with these problems cleanly?
Quite the opposite, we had tons of issues with npm/package.json and none whatsoever with maven, which we can use for basically everything and it's super stable from release to another.
The problem is also that there's several dependency managers in the ecosystem. You end up learning them all badly because some piece is only available in one or the other.
maven predates NPM by probably 20 years. We've learned a lot about dependency managers collectively in that time. I'm sure if maven were rewritten today, it would be different.
One thing I still miss in JVM is bundling the app into one executable binary, like others do (Go, Nim, Rust). I don't mean AOT compilation like GraalVM does, just either bundling needed JVM modules like jlink does (aka static) or depend on local pre-installed JVM (aka dynamic).
Yes, there's been jlink for a long time, but it still generates a folder, not a single file.
There is jpackage, but it generates installers for various platforms, not an executable binary.
Doing more work at scale these days and running into Elasticsearch, NiFi, and so forth.
I don't have to like the fact that java has to have its own keystore, and that the elasticsearch.service invocation has 35 (!) arguments by the time it reaches its launch. But these enterprise tools provide a methodology for getting granular when it makes sense.
I'm not sure why the size of a shared code library is important.
Adding components is about reusing code while replacing them is about having to track security issues (which is very good now, via maven) and new functionality being added (which you may or may not want). Same as NPM or any other package manager that existed.
I would say they should be the "right size". 27TB isn't one library. It's the amount of libraries there are.
In the NPM world there are too many small libraries that create a dependency graph that's impossible to parse mentally. No one knows your true dependencies and that created many problems.
In Maven libraries are sometimes too big. That's true. But most of them are closer to the size that gives a library a bit of "meat". Obviously YMMV.
27 TB is about the vast amount of choice in the ecosystem. Try to delete the node_modules folder vs the .m2 one on windows — only one of these chokes out a high-end laptop. In my experience Java dependency trees are not as absurdly deep as JS’s. Sure, frameworks do add quite a few nodes to the tree, but libs don’t have as many transitive deps as they do in JS, perhaps due to the better standard lib.
ResultSet rs = DB."SELECT * FROM Person p WHERE p.last_name = \{name}";
This when it becomes valid java will not allow an SQL injection attack. The name part would be properly passed as a parameter. Not a string concatenation.
Per https://openjdk.org/jeps/430
If that was not the example you where thinking about, sorry :)
Otherwise yes don't build parameterized SQL out of strings !
My main complaint about this new feature is that it has such poor conceptual clarity that people already familiar with Java will almost always misinterpret what is happening in exactly the same way. I've seen people post similar comments to the one you're correcting on reddit as well.
You are thinking of string interpolation. The idea behind string templates, the feature the example is showcasing, is effectively a way to define a function that can be called with similar syntax to string interpolation.
So DB."SELECT * FROM Person p WHERE p.last_name = \{name}" would basically be a function call like DB("SELECT * FROM Person p WHERE p.last_name = ", name), which would do the right thing to parameterize the SQL.
First of all, yes, new features alone can make something maintain popularity. Why would that not be the case?
Second, the author was using that as an example of Java showing a pattern of adding syntax improvements, which again might be something that maintains popularity.
Finally, from the article:
But let's talk about the Java we have today, not the one coming in 6 months.
It was just one talking point. Did you even read the article?
- use a library that implements RFC 5322 properly.
- don't validate the email address (that is, don't try to reject most invalid addresses) and just check that the field contains at least one `@` to be sure the user didn't enter something else than his email address by accident.
Tying to perform email address validation with regex you're writing yourself is the best way to reject perfectly acceptable email address and get frustrated users.
Check that its a string containing '@' with at least one character before and after the @ symbol is usually the only sensible thing to do.
The format for emails is surprisingly complex, and while you can use a regex to catch 99% of the right values, it's very difficult to get it 100% right. Often the best thing to do is just to try and send an email to the address a validation link.
This would reject valid email addresses using quoted parts like john."@".doe@valid.example.
Edit: Looking at it closely it looks like the attempt is to reject anything with an '@' in the user part, which would be an error (e.g. john."@".doe@valid.example). Except since "\S+" also includes '@' this regex will still work, but by accident. It would reject the valid email john."\ ".doe@valid.example
Sorry, I edited my comment and then noticed you replied already. It's working by accident since the '\S+' will match the second '@', but if you had a quoted space in the user it would not work e.g. john."\ ".doe@valid.example, but would work with john.@.doe@invalid.example which I believe the regex is author meant to be caught as invalid.
"new" keyword is basically introducing side effects, everywhere, making Java more difficult to test and confusing generations about how to do testing.
The primitive/object combination of int/Integer, et al is noise.
The reliance on annotations for modern development and the reluctance to put syntax in the language to codify obvious things. Basically requiring a pre-processing language (Spring) to do basic tasks, making development slower and more error prone.
The final keyword. The idea that you should sometimes take away developer agency, because you know the future, better than anyone. Classy.
The combination of Class Access Modifiers (which are for mixin property selection) with interface definition (what's this object's interface?).
The much acclaimed "java ecosystem" reinforces bad practices. 10 years later, PHP is derided for bad language choices but bad Java is handwaved as a series of missteps that has been corrected.
"new" keyword is basically introducing side effects, everywhere,
I don't think that's what it does. Unless you're talking about putting side effects into the constructor?
confusing generations about how to do testing.
Doesn't follow from the first part.
The primitive/object combination of int/Integer, et al is noise.
Somewhat agree.
The reliance on annotations for modern development
Sure, the Spring ones are horrible, but the Lombok and Jackson ones are pretty decent.
put syntax in the language to codify obvious things
What new syntax would you want? I guess I'd want to clean up the flatMap mess with some nice do-notation and <- arrows.
The final keyword. The idea that you should sometimes take away developer agency, because you know the future, better than anyone. Classy.
Final as a method keyword? Fair enough, but I don't think I've ever come across a method that I wanted to override but couldn't (I hate inheritance)..
Final is also to try to make things immutable, but making things immutable in Java also takes more care than it should.
The combination of Class Access Modifiers (which are for mixin property selection) with interface definition (what's this object's interface?).
I don't get this at all.
The much acclaimed "java ecosystem" reinforces bad practices. 10 years later, PHP is derided for bad language choices but bad Java is handwaved as a series of missteps that has been corrected.
> "new" keyword is basically introducing side effects, everywhere,
>> I don't think that's what it does.
From a java testing perspective, it does. There is no way to mock something that is new'd. This is a failure of the language. ie Python, p1 = MyClass()
Now you have something mockable.
> the Lombok and Jackson ones are pretty decent.
They are, but they aren't in the language because Oracle (and the existing language development practices). Actual improvements are haphazardly added to java because 3rd parties already did the work, so why should they make the language better? Just use the increasingly obscure set of 3rd party additions.
If you only work with SQL Server then it is probably best you use SQL Server Management Studio. If you use different databases you will need a different client and I find with JDBC based clients you can use one client for to connect to many databases. I have seen many here complain that the Java tools are slow and bloated and look ancient. From a looks perspective you can use FlatLaf themes which look good to me. There are some nice dark themes to chose from.
[0] https://dbeaver.io/ [1] https://www.sql-workbench.eu/ [2] https://github.com/squirrel-sql-client