If you were to give it a small facelift I wouldn't have to worry about all the tiny flaws that add up. I don't really need Kotlin, Scala or dead JVM language x. Just a slightly better Java.
If you were to give it a small facelift I wouldn't have to worry about all the tiny flaws that add up. I don't really need Kotlin, Scala or dead JVM language x. Just a slightly better Java.
1. Multiple (often unnecessary) layers of abstraction that end up being much more complicated than the thing they were intended to simplify through abstraction. There is no concept too simple to abstract it seems
2. Verbose and hard to follow "builder" patterns
3. The extreme proliferation of design patterns. I have literally seen hundreds in various Java codebases.
4. Gradle is great but requires memorizing a non-trivial API and learning a whole new (awesome, but still) language (Groovy).
5. There's always a "Java" way of doing stuff that is different (often because of abstractions, see #1) so you have to re-learn everything that way. Example: Managing TLS certificates
5. Legacy code is everywhere, and can be really painful.
So much of it was hacked together and became critical and can never be re-written and never retired
There are more, but I weary of this discussion :-)
Legacy code is everywhere on the world, only a selected few get to work on shiny things from scratch. Even cool startups eventually turn into legacy.
I wonder what technology used at enterprise level, doesn't suffer from those bullet points.
Wish that were the case, but unfortunately Gradle builds showed up in server-side, SpringBoot-heavy FinTech startup code I worked with not too recently. I guess it's due to the copy/paste nature of Spring development, like other choices in those code bases.
That is to be expected, given how Spring is now hyping Kotlin as they did previously with Groovy, Scala and Clojure support, and Kotlin folks tend to push for Gradle due to its use on Android.
It's the standard Java build tool at this point. I don't know of anything better.
I really don't see it anywhere else to make it standard, besides it depends on Maven Central.
Lots of fine pieces of software are used in small and big companies that do only one thing and do it well.
As for in house line-of-business apps, it's an elusive species because if it does what it needs to cost effectively and is not a marketing case for a fat software company like Oracle nobody often gets told about it. But I've seen and heard of LOB apps built thereupon that don't suffer from those bullet points, except for 5.
The same attitude existed in C++ programmers in that space that sneered at Java’s intrusion.
I see the same attitude in new teams using Golang in the enterprise.
"Why are we doing a Factory for Singleton Factory design pattern here?"
"Because this design pattern is made for this scenario"
Its pretty horrible, but by no mean java-specific. It can be achieved in any object-oriented language, and to some extent also in others.
Just avoid Spring. That's been my motto and I've been Spring free for 7 years now. Doctors say I should live out a happy life.
I do use package private fields liberally, though. Just not non-static public fields.
Which makes a lot of sense, if you look at it through the lens of cultural anthropology. Enterprises dictate approaches like this, because the alternative is leaving the choice to the individual programmer — and these same enterprises don’t hire experienced-enough programmers to trust their judgement. And so all the programmers who work for these enterprises end up absorbing this approach as a social norm, just “something you do”, rather than “independently rediscovering the need for it” in a way that would lead to them actually knowing when it’s useful. So, even when not locked into an enterprise that forces it on them, they just keep doing it, because that’s the culture that’s been inculcated on them (and how all the examples look, how all the libraries do it, etc.)
Personally, I’m not in theory against “unilateral” use of getters/setters, either. I just kind of which they worked in Java the way they work in Ruby: where referencing a field on any object other than `this` would actually just be sugar for a call to the getter/setter. Where the Java optimizer would then take special care to optimize-away the call frame for known-‘trivial’ getters/setters.
Beyond method call overhead (which may or may not be a given depending on how things are optimized in some languages), the caller doesn't know that automatically with explicit getters/setters, either; you'd have to read the source to know that just as much if they are explicit as implicit (or rely on documentation, which hopefully is complete and current).
1. MyClass has 10 private fields, each with a trivial getter and setter.
2. YourClass has 10 private fields, of which 9 have a trivial getter and setter. The last one has a trivial getter but the setter is non-trivial.
If you use your IDE to write your getters and setters, than the two classes above will look the same at glance.
If you use Lombok, the "special" field will stick out at once.
Personally, I prefer to avoid Lombok and just use public fields. That way, the field with a nontrivial setter will also stick out, and no need for bytecode magic.
- Ctrl + shift + T => search for types
- Ctrl + O => search for methods
- Ctrl + T => switch between classes
- Filters, https://help.eclipse.org/2020-12/index.jsp?topic=%2Forg.ecli...
In the early versions of Lombok it was a bit dodgy as it used private APIs, but now the compiler has the public hooks and its all good honest compiler plugins.
It’s a big world.
(Another weird thing for the first 25 years of my career never used anything but Sybase as a relational database — across 5 different firms. Never used Oracle or SQL Server.)
It's actually true that ruby makes it impossible to directly access fields in objects other than `self` (ie `this`). Seriously, it's not possible ruby takes away the ability to do so, thereby making it not even a choice anymore, you have to create a getter/setter, it's the only option. There is no sugar, there is simply a prohibition on direct access to iVars in other objects, period.
What might make it look like "sugar" is that parentheses method calls are optional in ruby -- `obj.foo` is just another syntax for `obj.foo()` Whether or not it's a getter, any method at all, nothing special for getter/setter here. That's literally the only thing going on here that makes you think there is "sugar", ruby syntax having parentheses be optional in method calls.
And additionally ruby gives you a shortcut "macro" to defining the getter/setter, `attr_accessor :foo` inside a class body just automatically defines the conventional getter and setter for you. You still do need to define them though, again no "sugar".
So ruby actually comes down solidly on the side of "yes, always use getters/setters"... in some ways this is what you are complaining about, people always doing it, right?
But it turns out differently and not annoying anyone, I think, because it was planned for by the language designers in the first place, both by making it mandatory (eliminating it as a point of debate or as something a programmer will spend any time ever considering when implmeenting), and by providing devices to make it convenient.
I think the choice to make parentheses optional in method calls actually goes along with the choice to forbid direct access to fields in other objects; both because there is now no need to syntactically distinguish between the two, and because it makes it less annoying to forbid. It also causes all sorts of other complexity in writing parsers for ruby though...
#instance_variable_get / #instance_variable_set is a thing. The (default) implementations of these methods on Object are just C code that do direct IVar reads/writes. You can thus think of #instance_variable_get and #instance_variable_set as real “field access” in Ruby. It’s just that, unlike most languages, there is sugar† for the getter/setter, and there isn’t any sugar for the “direct” field access.
† (The setter’s sugar is obvious — `a.b=x` is re-written by the lexer into a call `a.send(:"b=", x)`. The getter’s sugar is less obvious — it’s seemingly just a regular method call. But think about why Ruby makes parens optional on arity-0 method calls in the first place. To sweeten getters into something field-access-looking!)
But, well, you might say, #instance_variable_get is just a method, too! I can override it as I want! Really, it’s just another kind of getter/setter — sort of a categorical one.
Well, let’s go one level deeper — you can literally “reach into” the object to take a look at its IVars as it sees them:
obj.instance_eval{ @foo }
“Objection! #instance_eval can be overridden! DRb proxy-objects do it!”Well, okay, let’s get creative, and start breaking through the seemingly pure-OOP semantics of Ruby, revealing the more functional-language underpinnings:
inst_eval_fn = Object.instance_method(:instance_eval)
inst_eval_fn.bind(obj).call{ @foo }If you ever need to change something about that value, do additional logic before returning (I e. emit metrics), or model a data class with an interface having getter methods makes these so much easier.
But for some dumbass reason it's not "the Java way".
You have explicitly mark properties as mutable (“var” instead of “val”). I didn’t get the value of this at first, but when we started catching bugs at coding tiime that would have been runtime errors in Java, I started to understand. This was among my takeaways from working with it in production for about two years.
You have to go as far as Clojure does and create a whole new suite of truly immutable data containers, for full safety. I dunno if Kotlin goes this far. But yeah, it's the right way.
One factor I’ve thought about is how many Java programmers seem to have learned design by looking at things like the Java core libraries or a few open source frameworks, without recognizing that most people are not writing code intended to be generally reusable by millions of projects and that popularity means those projects cannot modernize their style easily, either. People write new code on new projects today as if it still needs to be compatible with the early 2000s because much of the code they learned from ossified around that time, and will claim it’s “best practice” because otherwise why would this Oracle/Apache API still use it?
p.x = p.getY() + p.z;
p.setY(p.x + p.z);
It's better to use more universal approach, so your code will look unified.Another thing why getters/setters are preferable is because when you need to replace field with computed value, you would need to replace all your code.
Proper approach would be to use properties which are available for every language but Java. They invented records, which somewhat fill that niche.
Also p.x = p.y + p.z is much easier read.
Another drawback of getters and setters is that you end up not looking at that code because you assume you know what it does, so you can miss when someone puts logic into a setter or getter
But nobody actually does it.
If you use getters and setters, you have more future options without breaking backward compatibility. When you are making libraries that lots of other projects might depend on, this is probably more important than when making apps, especially relatively small apps.
“Getter/setter from the start” optimizes for induced maintenance cost on dependent code.
Now, in some other languages, access via getters/setters is at least source-compatible with field access, which makes this less of (or not at all, for dynamic languages, or if it is object and not just source compatibility) an issue. But that's not the case with Java.
I think that's the key part here: most developers are writing code which will rarely or never be reused but they _learn_ by looking at shared code, and shared code which is on a different development cycle by a team nobody knows, and the field doesn't hasn't had a great history of discussing how different tradeoffs are appropriate for different projects.
public record SomeViewModel(String name, int age) { }Kotlin does have some nice additions. But it is very much a kitchen sink language. I wish I could take some of those nicer additions and leave the sink.
Of course there were added with awt 1.1 and swing in an attempt to catch up with the visual programming jazz and for many it never disappeared. Later on there was (still is) a trend to use builders... and a general shift for immutability.
It's basically what you want but unlike the other "dead" languages it is designed to complement Java rather than compete with it and is unlikely to ever become obsolete for that reason.
Meet Project Lombok. Problem solved and forgotten.
Immutables has its own set of problems, but at least it uses the standard Annotation processor and has a clear separation of generated code and the interface you write.
Haven't used Java's new record's yet, so don't really know anything about that.