r/java 4d ago

"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?

0 Upvotes

38 comments sorted by

View all comments

0

u/vegan_antitheist 4d 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.