"Deep Magic" in Java
I've been a Java developer for a few years, and the thing that keeps driving me nuts is that reading code is nearly useless, the real code is hidden from you. It's almost like the language isn't made for reading code but reading javadocs instead.
You call "Arrays.asList(arr)" and it returns you an instance of java.util.Arrays$ArrayList, a class with the same name as java.util.ArrayList except it is fixed size, meaning that trying to resize it will get you a runtime exception thrown. The method signature says it will return a List, but the moment you call it without the elite ball knowledge it will either stab you in the back or explode in your hands.
And nearly every time you try to prevent this by reading the implementation it sends you to a wild goose chase through 6 different one-liner wrapper methods across 5 different thousand-line files.
It gets even worse at the enterprise level: I once bloated my system by writing a submit button's logic in the submit button's method.
I then discovered it was because the method was in a class with a magical 'ViewScoped' annotation and I was supposed to delegate some of that logic to a field that had a 'Stateless' annotation.
Any ordinary reading of the code would say that that field is part of the class' state, but because of the annotations it was actually a resource pooled by the container, independent from the view class which is only supposed to cache UI data for each given user.
Didn't take long for me to get corrected. You can imagine the serene smug on my senior's face as he dropped that piece of esoteric knowledge on me, while I was looking back in horror.
You guys probably heard this a thousand times, meanwhile I'm still learning new words in my console output. Any advice?
•
u/Minecraftian14 3d ago
I personally enjoy reading Java source code.
I have had my hard times, for example, Stream#collect(Collectors.toMap(..., ...)) will throw an exception if there are duplicate entries for keys.
But in the end, it was documented, and has alternative signatures to handle such data.
Most of the sources like Collections and Swing was fun reading. Streams itself is crazy though. Nevertheless, i enjoy the journey.
•
u/awidesky 3d ago
Me too.
I used to think Java's code is hard to understand because of genetics and deep class hierarchy, but after I tried decrypting those horrible templates and macros and typedefs in C++ stl implementation...
Reading through Java implementation helped me a lot about understanding data structures, and interface relationships.•
u/Minecraftian14 3d ago
Exactly!!! I once was looking into libc of LLVM. Geez.
While i understood most of it by reading, it took specifically more time. Plus, if I were to write it out, i could never match that style. Too much operator overloading and those crazy templates, are just ridiculous. And not to even talk about how pointers play out there.
As a matter of fact, python can at times be worse (mostly Good though). Often when looking for the sources, i find there's only a meetings stub, and not it's actually implemention. Other times it's a function calling another calling another. This is especially worse in tensorflow
•
u/Yahim0 2d ago
> I personally enjoy reading Java source code.
> But in the end, it was documented
¯_(ツ)_/¯•
u/Minecraftian14 2d ago
> I've been a Java developer for a few years
> Arrays$ArrayList, a class with the same name as ArrayList
¯_(ツ)_/¯
•
u/gletschafloh 3d ago edited 2d ago
TLDR: i didnt read the instructions and used a hammer instead of a screwdriver to drive a screw into the wall
•
u/simonides_ 3d ago
There are always things that come with experience in any language.
Assuming the resize is a normal thing happening you should have seen it in your unit test.
•
u/killa1093 3d ago
For the Arrays.asList situation, the javadoc clearly states it returns a fixed size list. I don't see the confusion here
•
u/Ok_Elk_638 2d ago
Personally I have never had a problem with Java code or with the JDK. If I have to gripe about the Java ecosystem then I would say the excessive use of annotations. Everybody everywhere constantly wants to define and use tons of annotations everywhere. As far as I can see they are only actually good for telling serializers what to do with data. But Java programmers seem intent on decorating a single line of Java code with 20 odd annotations.
•
u/OddEstimate1627 22h ago
Annotation processors are IMO one of the best features of Java. They can be overused and make some frameworks look like a different language though.
•
u/Ok_Option_3 3d ago
returns you an instance of java.util.Arrays$ArrayList, a class with the same name as java.util.ArrayList except it is fixed size, meaning that trying to resize it will get you a runtime exception thrown.
The question on how to handle immutable collections is as old as the collections API itself and still hasn't been answered!!
Rather than see this as an aberration, see it as a learning opportunity. C++ solves this is const, Apache collections has a whole immunltable set of interfaces. Why doesn't the standard JVM have a solution yet? What have experts said?
•
u/vegan_antitheist 3d ago
I think they don't mind. The interface just gives you the methods a collection might implement. Interfaces for immutability would be worse IMO. They should have just given us a basic List interface for anything that is a sequence of elements and then a MutableList interface for those that are explicitly mutable.
You can't just trust an interface unless it's sealed, which would make it impossible to implement it yourself. So you have to create a defensive copy anyway.Instead, they just give us List, which makes it look as if it was mutable, because it has those methods. On the other hand, it's not really an issue. Only if you just created the List (using ArrayList) you can be sure it's mutable. In any other case you just have to assume it's not. Would more types really make any of this easier? Even if you could test (list instanceof MutableList<?>), you wouldn't know if that list was shared with other threads, so mutating it would still be too dangerous. Nothing would actually get better with more interfaces.
It's boils down to this:
- If and only if you have created a mutable list (i.e. ArrayList) and you have not shared that list with any other code, you can be sure that it's ok to mutate that list.
- In any other case, you have to assume it's immutable or that mutating it would not be safe.
•
u/ryan_the_leach 3d ago
Just curious, what is the point of an immutable list from an array?
Surely the most common usage of creating a list from an array, is because you intend to start editing it, and you need the performance characteristics of a list rather then an array while you perform those edits?
Or is it more a compatibility shim, when you need to pass an array to a method that has a list in its signature?
•
u/vegan_antitheist 3d ago
You get an object that implements that List interface. That's all it does. You can then apply any method that requires a List. Why would you modify a list that gets passed around?
•
u/ryan_the_leach 3d ago
Seems like you just doubled down on what you were originally saying, rather then reading my comment.
•
u/vegan_antitheist 2d ago
I read it and I disagree[edit:] with your first point, the last part is correct [/edit]. You can modify arrays. You wrap it to get a List when some API asks for that and you don't need a defensive copy.
•
u/Yahim0 2d ago edited 2d ago
A bunch of people here telling me to just read the documentation, which kinda proves my point.
I should probably be doing more of that anyways, although I still have to "decode" the documentation a good portion of the time which annoys me quite a bit.
And yes, I do understand the concept of a trade-off.
Thanks for the replies, regardless.
•
u/pgris 20h ago
Honestly, I always hated the 'java.util.Arrays$ArrayList' vs 'ArrayList'
I would have beeing so easy to name it FixedSizeList and make it less error prone.
Regarding the 'magical 'ViewScoped' annotation', yes, you are expected to know the framework you are using.
'the language isn't made for reading code but reading javadocs instead'
Yes, this is very much true. That is a good thing. Sadly, it only applies to the well known and well programmed frameworks, (and the JDK). Tipically the code your company writes has horrible, out of date documentation, if at all, and you need to read the code.
•
u/quantum-fudge 16h ago
I came here hoping I'd learn something cool and obscure, but instead got a rant on basic APIs :/
•
u/brian_goetz 14h ago
In addition to all the comments about "read the docs", note that you are also conflating "Java" (the language, the platform, the runtime) with "random frameworks I found in the Java community". There is no aspect of Java that gives semantics to annotations, so if you are confused by an annotation-driven framework that is too "magic" or too poorly documented, don't blame Java, blame that framework (or the person who selected that framework for your application, which might be you.)
•
u/idontlikegudeg 3d ago edited 1d ago
That's one thing that's great about Java: everything is properly documented and plays nicely together.
Arrays.asList() is documented as returning an unmodifiable fixed size List that is a view of the array passed in, meaning you cannot modify the size of the List, but changes to the original array will be visible in the list. That's about all you need to know. Everything beyond that, if the concrete class is called ArrayList, an anonymous class or whatever is an implementation detail that should be of no concern to you.
Relying on implementatioin details will give you nothing but trouble unless you know exactly what you do and have a very special reason to do so (and trust me, most of the time, you have not).
Just look at the String class. The internals have been significantly changed over the past years. The String class of modern Java versions is implemented completely different from what we used to have. And yet, your code compiled under Java 6 still runs without the slightest modification and recompilation, while at the same time using half the memory in many cases, and in general performing much better.
EDIT: I wrote unmodifiable, but only the size is unmodifiable. Thanks for the correction.
•
u/SleepingTabby 1d ago
"Arrays.asList() is documented as returning an unmodifiable List"
It's not documented like that at all - because it is actually NOT unmodifiable. You can still replace the List's elements, the List::set method works perfectly fine.
"meaning you cannot modify the List"
The documentation explicitly says you can :P
•
u/idontlikegudeg 1d ago
Ouch. Yes, you are of course right. Shane on me. It’s documented that you cannot change the size.
•
•
u/jedilowe 3d ago
Why are you using arrays at all? It's like putting your bike in the back of a pickup truck but still wanting to use the bike. Just drive the truck! The as list just lets you read an array using list calls, not a full featured data structure.
I think part of what you are describing is the disconnect between college professors (especially CS) very simplistic problem sets versus those of the real world. It's not even that they can't understand the complex (though a large number have never worked a production product). Its just the classes are too focused on traditional basics that nobody needs because of libraries like Java's. Leetcode should be laughed at by professional SWEs but we all seem to rever interviews from FAANG or whatever who themselves rarely build useful software for practical uses outside their one niche app.
•
u/Yahim0 2d ago
Idk, something gave me an array and I'm trying to make a list out of it I guess
•
u/jedilowe 2d ago
If you get an array you can always add it to a new list and use that if you need to modify it. Then if you need to send back an array you can convert it back. It's better to use your own data anyway as you know you control it
•
u/vegan_antitheist 3d ago
java.util.Arrays$ArrayList, a class with the same name as java.util.ArrayList
Those are different names
meaning that trying to resize it will get you a runtime exception thrown
interfaces in Java collections framework always give you an API for all methods the collection might implement. You never know if they really are implemented. I don't really like that but it's how it is. If you want to to be resizable, you have to create a type that is known to support it. A list based on an array obviously can't do that.
The method signature says it will return a List,
Which it does.
but the moment you call it without the elite ball knowledge it will either stab you in the back or explode in your hands.
Why would you assume that something returned from a method is mutable? One issue Java actually has is that mutability used to be the default. In general, Java used to have ( and still has in many cases) the exact opposite as the default from what it should be. For example, instead of "final" (which has 4 very different meanings, but that's a different issue) we should have keywords that do the opposite.
If you want something weird, like it being mutable or allowing null, the it's up to you to specify just that.
And nearly every time you try to prevent this by reading the implementation it sends you to a wild goose chase through 6 different one-liner wrapper methods across 5 different thousand-line files.
Then don't do that. Don't try to "prevent" some issue that you can can circumvent by not making false assumptions about returned collections and instead use something you have control over. They all have copy constructors. So, use them.
I once bloated my system by writing a submit button's logic in the submit button's method.
What are you talking about? A button is something in the UI. Logic doesn't belong there. What even is a "submit button's method"?
I then discovered it was because the method was in a class with a magical 'ViewScoped' annotation and I was supposed to delegate some of that logic to a field that had a 'Stateless' annotation.
Sounds to me like you just didn't understand what the framework does and now you want to blame it on others.
Any advice?
- Java defaults: They are just bad (actually, the opposite of what they should be) but they are trying to fix some of them. For example, they already got rid of the "break" in modern switch statements, and we will have variables that explicitly are or aren't nullable in the future. But we they can't get rid of "final".
- Frameworks: Learn how the framework actually work and what it wants from you when you implement the Beans and other types that will be used by it. Don't just copy paste code, but learn what the annotations do. Debugging is often difficult, but when you keep it simple and understand its fundamentals, it's not that hard.
•
u/gjosifov 3d ago
reading code is nearly useless, the real code is hidden from you
There was a time when the annotations didn't exist and the code for executing HTTP GET to DB was huge
a lot of inheritance, a lot of xml and the phrase Java bloat was born
Since 2014 or Java EE 8 and Java 8 that same functionality can be achieve with only 1 class and 3-4 annotations and the phrase Java is too much magic was born
Everyone wants big salary, but it is hard to read the documentation and learn to debug
Maybe programming isn't for you, maybe you can try to work at a fast food restaurant or be a barber
it is easy job - after 3-4 years you learn all the tricks you need and updating your knowledge is max 1 week / year effort
Try different job, programming isn't for everyone it is too much bloat or too much magic
•
u/javaprof 3d ago
Java's core library violates liskov substitution principle, everyone who wrote app in Java knows it and deal with it
•
u/SleepingTabby 1d ago
Does it though? Collection::add specifies that it throws an exception when it refuses to add an element (for any other reason that "the collection already contains this element"). This has been in the docs since Java 5, i.e. the introduction of Collections
•
u/javaprof 1d ago
So all the code that accepts collection interfaces handles exceptions on add? Good joke, most of the time it just violates LSP and blow up at runtime. Having "optional" methods in interfaces was a mistake, especially for this very common case
•
u/pradeepngupta 3d ago
Java is a collections of Classes and API. Whenever you want to use specific class or api, read javadoc and write sensible code. If api says an exception might throw, be ready with the exception handling as well. It takes time for a beginner to learn java coding. As a beginner it took ne 2 years to learn java properly and then I started with multi threaded programming. Now as well, after 22+ years of experience in Java, i cannot say that I am perfect. I always relies on Javadoc even today. And of course, AI generate full codebase, snd while reviewing code, i still go line by line and see whether it generated intended code or hallucinated. And javadocs plays important role even today. Infact my work for writing a full book on java is in progress, the book named Buzzing Java Willson be published,it is written for senior engineers and architects - what they need to take it care while designing an enterprise systems in java abd in the age of AI. DM me if anyone interested to know more about the book Buzzing Java.
•
u/awidesky 3d ago
You don't need to read the internal implementation to know that, since the api) clearly states: "Returns a fixed-size list backed by the specified array."
My suggestion is to always read the api.
Java's api document so informative and detailed compared to Python's or C/C++'s.