Originally published on www.gunnargissel.com
Immutable objects are objects that don't change. You make them, then you can't change them. Inste...
For further actions, you may consider blocking this person and/or reporting abuse
If you are using Java 10 or later, you can make defensive copies of collections using functions like
List.copyOf
andMap.copyOf
.They make a copy which is inherently immutable. The beauty is, if the collection you are copying is already immutable, it will usually just return a reference to itself instead of making another (needless) copy.
I've been using the immutable collection classes in Google's Guava library to do the same thing for years.
docs.oracle.com/javase/10/docs/api...
google.github.io/guava/releases/sn...
Haven’t really played with 10 yet - that sounds really handy!
Love Guava, but I like to see how far I can get without including libraries. Guava is the library that I wish was built in 😍
This might be considered too defensive but I don't really like the immutable collections because there is no compiler safety for misuse - only runtime errors (same goes for singleton, empty collections). The
AbstractList
implementation ofadd
(i.e.throw new UnsupportedOperationException
...) is a good example of a refused bequest code smell.With hindsight the designers would probably have removed mutable methods from the
java.util.Collection
interface and introduced another interface/contract intended for use by concrete implementations concerned with mutability.This issue can be compounded when working with third party code if you do not have access to sources so (without decompiling) you are forced to rely on sensible design or good documentation.
In addition I have seen code like this:
Whilst it is a slight memory optimisation use the empty list (instead of instantiating and returning empty list) it also creates a nasty trap for the caller. Worst case this leads to a game of Charades where all lists are wrapped as new lists "just in case" ... leading to bigger allocation/memory overheads.
So my general approach is to either:
That makes the behaviour safer and more predictable.
All good points and interesting things to consider
If I remember correctly having optionals as fields is an antipattern or at least not really good practice. However I don't recall why.
What's the advantage in contrast to simply wrapping the object in the getter:
public Optional<int> getWeight() {
return Optional.ofNullable(weight);
}
?
Besides my question. Great article dude!
Thanks for reading!
What I've read is that people advise against taking Optional as an argument (this is a pretty fair example of the argument). I disagree with that, or at least I weight my considerations differently.
I think taking Optional as an argument clearly signals to the user of the class what the implementer of the class is expecting in terms of required fields. I feel that being clear to the users of the code is generally more important than being kind to the compiler or performance, but all of this is subject to the actual use case.
My unstated assumption is that everything is setup to fail immediately if a null is passed around. For example:
vs.
To me, that seems better than the normal null checking dance we Java programmers go through.
How does it compare to wrapping the object getter? I've tried it that way, and in terms of using the class it's fine. My biggest complaint is you can't just code gen the getters if you do that. I also taking Optional as an argument clarifies code enough to outweigh the disadvantages in most situations.
I guess I should also say that all this Optional stuff is in context to the immutable object. I'm a little on the fence with Optional in the builder, not sure how I feel one way or another.
Here's one from Brian Goetz that a lot of people find convicing
I still disagree, obviously not everyone at Oracle is on board, so....
¯\(ツ)/¯
Thanks for the explanation. In general I agree at least with your readability over performance thingy.
I'd still go for the getter way tho. However I agree that using an optional as argument can be ok. I would still prefer doing something like throwing an illegal argument exception because npes are kinda ugly. Also to remove that if else clutter you could wrap the arg2 in an optional and map or else.
However that is not really related to immutability. I liked the article :)
If you make the Builder a static inner class then you can give the class you're building a private constructor that takes an object of the Builder class. So you end up with
final Dog dog = ImmutableDog.builder().name("fido").weight(20).build();
Doing it this way enforces using the builder and I've found it helps with maintenance
You might have noticed that I don't bother with get/set prefixes on Builders - they're not Beans and I'm probably never going to read a value that I'm putting into the builder.
That's a nice way of doing it, and I prefer creating the immutable class from the builder.
I've found getters useful on builders for doing the final validation before creating the real immutable object - how do you usually handle validation?
A mix of in the not-a-setter method and the build method before calling the constructor (and maybe occasionally in the constructor, but not by preference). Depends on exactly why it's not valid.
There's also a nice library for creating Immutable objects - immutables.github.io/
Nice article! However I'm a bit confused. As far as I know it's impossible to use generic types with primitives in Java. Therefore following code won't compile:
private final Optional<int> weight;
instead you should type:
private final Optional<Integer> weight;
am I right? Or maybe it's written in newest version of Java and in Java 13 it's OK?
Nice article! However I'm a bit confused. As far as I know it's impossible to use generic types with primitives in Java. Therefore following code won't compile:
private final Optional<int> weight;
instead you should type:
private final Optional<Integer> weight;
am I right? Or maybe it's written in newest version of Java and in Java 13 it's OK?
How to prevent immutability of objects during serialization and invocation through reflection APIs ?
I wrote this with the mind that users would respect the api and try to break the contract at their own peril.
I'm trying to think of why you'd want your object to be immutable through serialization, and my guess is that you want an immutable singleton? Here's a blog post that covers a bit on dealing with serialization in that case: lingpipe-blog.com/2009/08/10/seria...
As far as reflection goes, you can't get away from it. It's built in. If someone really wants it, they could write some groovy code and ignore access modifiers altogether. Whatever happens after a downstream user brings in reflection to defeat immutability is kind of on them, imho
Thanks. The blog is useful