Date post: | 15-Jan-2017 |
Category: |
Education |
Upload: | marcus-biel |
View: | 539 times |
Download: | 0 times |
The concept of Immutability has always been very important
across all programming languages.
MARCUS BIEL,Software CraftsmanImmutables in Java
MARCUS BIEL,Software CraftsmanImmutables in Java
For a Java developer however, Immutable classes, or simply, "Immutables",
MARCUS BIEL,Software CraftsmanImmutables in Java
have become more important than ever with the release of Java 8.
MARCUS BIEL,Software CraftsmanImmutables in Java
Among many other cool things, this version introduced the concept of
functional programming as well as the new java.time api.
MARCUS BIEL,Software CraftsmanImmutables in Java
Immutable classes play a key role in both of these new features.
MARCUS BIEL,Software CraftsmanImmutables in Java
Because of this, I’ve decided to provide you with
a detailed look at Immutables.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Outline
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
I'll tell you exactly what an Immutable is,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Outline
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
its advantages and disadvantages,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Outline
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
and how to create an Immutable.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Outline
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
I’ll finish by giving you concrete advice on when to use Immutables in your daily work.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
What is an Immutable?
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
So let’s start with the most important question – what is an Immutable class?
Copyright © 2016 Marcus Bielwww.marcus-biel.com
What is an Immutable?
In short, an Immutable class is a class whose instances
cannot be modified.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
What is an Immutable?
The information contained in each immutable object is provided when it is created -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
What is an Immutable?
and is frozen for its lifetime. Once an Immutable object has been created,
it is read only, forever fixed, like a fossil.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
What is an Immutable?
USS Enterprise (NCC-1701)
ImmutableSpaceship enterprise = new ImmutableSpaceship("Enterprise");enterprise.exploreGalaxy();
So if an object is immutable, how can we modify it?
Copyright © 2016 Marcus Bielwww.marcus-biel.com
What is an Immutable?
USS Enterprise (NCC-1701)
ImmutableSpaceship enterprise = new ImmutableSpaceship("Enterprise");enterprise.exploreGalaxy();
How can we change this unchangeable spaceship? How can we explore strange new worlds and “boldly go where no man has gone before”?
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }
public final class ImmutableSpaceship { private final String name; private final Destination destination; public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
What is an Immutable?
As it turns out, we can’t. As I've said, you cannot change an Immutable.
Instead, you can return a new Object that does reflect the change.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }
public final class ImmutableSpaceship { private final String name; private final Destination destination; public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
What is an Immutable?
In this case we’d return a new ImmutableSpaceship object with
a new Destination value of “OUTER_SPACE”.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Outline
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
So far this seems useless. Why should you bother with Immutables?
What kind of advantages will they bring you?
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Advantages of Immutables
• Are stable & fault tolerant
First of all, Immutable classes greatly reduce the effort needed to implement a stable
and fault tolerant system.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Advantages of Immutables
• Are stable & fault tolerant
After object creation, Immutables can be in only one state, which seems very confining at first,
but it’s actually extremely beneficial. Let’s see why.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
Imagine we have to implement a bank's accounting software.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
As a result of the financial crisis, the bank doesn't want its customers to be in debt.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
In other words, there is a business rule that
an account balance must never be negative. Such a rule is called an invariant.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
private void validate(long balance) { if (balance < 0) { throw new IllegalArgumentException("balance must not be negative:"+ balance); }}
To implement this rule, we will add a validation method that gets called
whenever the balance is changed.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
private void validate(long balance) { if (balance < 0) { throw new IllegalArgumentException("balance must not be negative:"+ balance); }}
Immutables are stable & fault tolerant
In case of an attempt to overdraw the account,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
private void validate(long balance) { if (balance < 0) { throw new IllegalArgumentException("balance must not be negative:"+ balance); }}
an IllegalArgumentException will be thrown.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
As you probably can imagine, the bank wants us to implement
a variety of functions for their clients,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
for example withdraw(), payDebt() and
transferMoney().
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]} To enforce the rule that the account’s
balance must never be negative, we have to call the validation method from all these
methods.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]}
This seems like a lot of duplicated code. There must be a better way!
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]} Pause here and think of an
alternative way to make sure that none of these methods would overdraw the account’s balance.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]}
Okay, the truth is, I tricked you a bit. We don't actually need to validate the balance in each
method.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]}
I said we need to validate the object whenever it is changing,
but an Immutable is not changing.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]}
Immutables are stable & fault tolerant
Instead, we validate the balance once in the constructor, so that no invalid object can be constructed.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]} Once validated,
an immutable object will stay valid for its entire lifetime.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]} WOW! This is really cool!
Methods like withdraw(), payDebt() and transferMoney() will return a new object, which will again call the constructor and validate the new balance.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) {…}
public ImmutableAccount payDebt(long amount) {…}
public ImmutableAccount transferMoney(long amount) {…}
[…]}
Immutables are stable & fault tolerant
But wait, it gets even better! An Immutable remains consistent – even in the case of an exception.
Let me give you an example:
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
Imagine you go to the ATM -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
- to get some cash.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
You put in your bank card, you type in your PIN.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
Balance:
+20,000.00
The bank takes the money …
Copyright © 2016 Marcus Bielwww.marcus-biel.com
0.00Balance:
Immutables are stable & fault tolerant
out of your account. But just before it gets into your hands,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
there’s an issue with the ATM. The money has already been taken out of your bank
account, but it isn’t coming out of the machine.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
0.00Balance: So now, you can kiss your money good bye.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
0.00Balance:
Unless the account has been implemented as an Immutable.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
Balance:
+20,000.00
In this case, your balance will be in the same state that it was before the failure occurred. Let’s see what this looks like in code.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) { long newBalance = newBalance(amount); return new ImmutableAccount(newBalance); }
private long newBalance(long amount) { }[…]}
Immutables are stable & fault tolerant
If we are trying to withdraw money from our ImmutableAccount class,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables are stable & fault tolerant
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) { long newBalance = newBalance(amount); return new ImmutableAccount(newBalance); }
private long newBalance(long amount) { // exception during balance calculation
}[…]}
and an exception occurs,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) { long newBalance = newBalance(amount); return new ImmutableAccount(newBalance); }
private long newBalance(long amount) { // exception during balance calculation
}[…]}
Immutables are stable & fault tolerant
a new ImmutableAccount object will never be created and
the original bank account object will stay unchanged and be saved.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) { long newBalance = newBalance(amount); return new ImmutableAccount(newBalance); }
private long newBalance(long amount) { // exception during balance calculation
}[…]}
Immutables are stable & fault tolerant
So as I’ve shown you, an Immutable object can never get into an inconsistent
state, even in the case of an exception.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) { long newBalance = newBalance(amount); return new ImmutableAccount(newBalance); }
private long newBalance(long amount) { // exception during balance calculation
}[…]}
Immutables are stable & fault tolerant
This stability comes at no cost, apart from the cost of the initial validation.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) { long newBalance = newBalance(amount); return new ImmutableAccount(newBalance); }
private long newBalance(long amount) { // exception during balance calculation
}[…]}
Immutables are stable & fault tolerant
It's based on the simple fact that an Immutable cannot change after object creation.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableAccount {
private final long balance;
public ImmutableAccount(long balance) { validate(balance); this.balance = balance; }
public ImmutableAccount withdraw(long amount) { long newBalance = newBalance(amount); return new ImmutableAccount(newBalance); }
private long newBalance(long amount) { // exception during balance calculation
}[…]}
Immutables are stable & fault tolerant
Ok, I hope you haven't fallen asleep yet. There is so much more that Immutables have to offer,
so let’s go on.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
• Are stable & fault tolerant• Can be shared freely
Since Immutables don’t change, they can be shared freely. Let’s see what this means.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
Balance
AccountAccount
Holder HolderBalance
In this example, two Mutable Account objects are
sharing the same Balance attribute.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
AccountAccount
Holder HolderBalance
If the Account object on the right
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
AccountAccount
Holder HolderBalance
changes the balance, this will also indirectly influence the Account object on
the left.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
AccountAccount
Holder HolderBalance
If, however, the balance attribute is immutable,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Holder
Account
Immutables can be shared freely
Account
HolderBalanceBalance
when the Account object on the right tries to change the balance object, it will not change,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Account
Balance Holder
Immutables can be shared freely
Account
Holder Balance
but return a new object instead. So there will be a second balance object now.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Account
Balance Holder
Immutables can be shared freely
Account
Holder Balance
For the same reason - an Immutable does not need a copy constructor when copying an object.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
If you don’t understand what this means or you’d like to learn more about cloning and copy constructors,
check out my video “Shallow versus Deep Copy”.
http://www.marcus-biel.com/shallow-vs-deep-copy-video-tutorial/
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
• Are stable & fault tolerant• Can be shared freely
Immutables can even be shared freely, when using a lock free algorithm in
a multithreaded environment, where multiple actions happen in parallel.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables can be shared freely
• Are stable & fault tolerant• Can be shared freely
Many see this as the key benefit of Immutables. However, it is a very advanced subject,
so I won't go into detail here, but will probably show it at a later stage.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables work well as Map keys and Set elements
• Are stable & fault tolerant• Can be shared freely• Work well as Map keys and Set
elements
Finally, Immutable objects are also a perfect option to use as Map keys and Set elements,
since Map keys and Set elements must never change.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables work well as Map keys and Set elements
These are the main advantages of Immutables. Let’s also look at their disadvantages.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Disadvantages of Immutables
• May lead to performance problems
The biggest disadvantage of Immutable classes is that their use may lead to performance problems.
Let's see how.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables may lead to performance problems
Object
Immutables require a new object
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables may lead to performance problems
for every distinct state they represent.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables may lead to performance problems
Therefore, the use of Immutables often increases the number of objects created.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables may lead to performance problems
Naturally, the more objects that are created, the more system resources will be used.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Immutables may lead to performance problems
However, this may not be a problem at all. There are many more influencing factors
as we will see later on.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
How to create an Immutable
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
So now that I’ve shown the value of an Immutable class. How can we actually make one?
Copyright © 2016 Marcus Bielwww.marcus-biel.com
How to create an Immutable
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
To make a class immutable, we have to follow a few rules:
Copyright © 2016 Marcus Bielwww.marcus-biel.com
1. Make all attributes private final
1.Make all attributes private final.
First of all, we have to make all its attributes private and final
Copyright © 2016 Marcus Bielwww.marcus-biel.com
private final String name; private final Destination destination;
1. Make all attributes private final
public final class ImmutableSpaceship {
public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; } public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }[…]}
Let’s illustrate this by looking at the ImmutableSpaceship class I introduced you to before.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
1. Make all attributes private final
private final String name; private final Destination destination;
public final class ImmutableSpaceship {
public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; } public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }[…]}
We make the attributes private, so that no reference can be accessed from outside.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
1. Make all attributes private final
private final String name; private final Destination destination;
public final class ImmutableSpaceship {
public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; } public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }[…]} While private variables cannot be
accessed from outside the class, they can still be reassigned.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
1. Make all attributes private final
private final String name; private final Destination destination;
public final class ImmutableSpaceship {
public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; } public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }[…]}
To prevent this we also have to make all variables final.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
1. Make all attributes private final
private final String name; private final Destination destination;
public final class ImmutableSpaceship {
public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; } public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }[…]}Setting all internal reference variables to final –
clearly communicates our intent to make an immutable class.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
1. Make all attributes private final
private final String name; private final Destination destination;
public final class ImmutableSpaceship {
public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; } public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }[…]}
Should someone try to reassign a reference variable, a compiler error will occur.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
2. Don’t provide any methods that modify the object’s state
1.Make all attributes private and final.2.Don’t provide any methods that modify
the object’s state.
Okay. Second, we must not provide any methods
that modify the object’s state.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
2. Don’t provide any methods that modify the object’s state
1.Make all attributes private and final.2.Don’t provide any methods that modify
the object’s state.
This I have already briefly discussed at the beginning of this presentation, but now we will look at it more closely.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }
2. Don’t provide any methods that modify the object’s state
public final class ImmutableSpaceship { private final String name; private final Destination destination; public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
For any change that you want to apply to your Immutable object,
you have to provide a method that returns a new object.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
2. Don’t provide any methods that modify the object’s state
public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }
public final class ImmutableSpaceship { private final String name; private final Destination destination; public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}Attributes that do not change, like “name” in this case,
can be copied from our current object.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
2. Don’t provide any methods that modify the object’s state
public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }
public final class ImmutableSpaceship { private final String name; private final Destination destination; public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
Attributes that do change, like “destination” in this case, have to be initialized with a new value instead.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
3. Ensure that the class can’t be extended
1.Make all attributes private and final.2.Don’t provide any methods that modify
the object’s state.3.Ensure that the class can’t be extended.
Let’s go on to the next rule. To further protect our class from being changed, we also have to prevent it from being extended.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
3. Ensure that the class can’t be extended
1.Make all attributes private and final.2.Don’t provide any methods that modify
the object’s state.3.Ensure that the class can’t be extended.
Extending a class would allow you to override its methods.
This would allow you to directly change the “unchangeable" object.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
3. Ensure that the class can’t be extended
1.Make all attributes private and final.2.Don’t provide any methods that modify
the object’s state.3.Ensure that the class can’t be extended.
Let's look at a code example to better illustrate this:
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public class RomulanSpaceship extends ImmutableSpaceship {
@Override public RomulanSpaceship exploreGalaxy() { this.destination = Destination.OUTER_SPACE; return this; }
3. Ensure that the class can’t be extended
[…]}
private String name; private Destination destination;
public RomulanSpaceship(String name) { super(name); }
So here we have a RomulanSpaceship extending our ImmutableSpaceship.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
@Override public RomulanSpaceship exploreGalaxy() { this.destination = Destination.NEUTRAL_ZONE; return this; }
public class RomulanSpaceship extends ImmutableSpaceship {
3. Ensure that the class can’t be extended
[…]}
private String name; private Destination destination;
public RomulanSpaceship(String name) { super(name); }
Its overridden exploreGalaxy() method violates the rules of an Immutable,
as it directly changes the destination attribute.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
ImmutableSpaceship immutableSpaceship = new RomulanSpaceship("Battlequeen");
3. Ensure that the class can’t be extended
As RomulanSpaceship extends ImmutableSpaceship
Copyright © 2016 Marcus Bielwww.marcus-biel.com
3. Ensure that the class can’t be extended
ImmutableSpaceship immutableSpaceship = new RomulanSpaceship("Battlequeen");
we can create an instance of a RomulanSpaceship,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
3. Ensure that the class can’t be extended
ImmutableSpaceship immutableSpaceship = new RomulanSpaceship("Battlequeen");
and assign it to an ImmutableSpaceship reference variable.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
ImmutableSpaceship immutableSpaceship = new RomulanSpaceship("Battlequeen");[…]immutableSpaceship.exploreGalaxy(); // internally changes the "immutable" Spaceship
3. Ensure that the class can’t be extended
Much later in the code, in a different class and method, we will call the exploreGalaxy() method on our ImmutableSpaceship instance -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
ImmutableSpaceship immutableSpaceship = new RomulanSpaceship("Battlequeen");[…]immutableSpaceship.exploreGalaxy(); // internally changes the "immutable" Spaceship
3. Ensure that the class can’t be extended
which, much to our concern, will internally change the spaceship instance,
which we expected to be unchangeable.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public final class ImmutableSpaceship {
3. Ensure that the class can’t be extended
private final String name; private final Destination destination; public ImmutableSpaceship(String name) { this.name = name; this.destination = Destination.NONE; } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; } public ImmutableSpaceship exploreGalaxy() { return new ImmutableSpaceship(name, Destination.OUTER_SPACE); }[…]}To prevent this, we just have to make our class final.
A final class can’t be extended, so it won’t be possible to override any method either.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
1.Make all attributes private and final.2.Don’t provide any methods that modify the
object’s state.3.Ensure that the class can’t be extended.4.Ensure exclusive access to any mutable
attributes.And now to the final rule – we have ensure exclusive access to any mutable
attributes.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Here you can see a representation of our spaceship object.
Name
Spaceship
DestinationName
Immutable
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
The attribute “name” is of type String, which is an Immutable.
Name
Spaceship
DestinationName
Immutable
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
As I've already shown you, you can freely share Immutable attributes -
Name
Spaceship
DestinationName
Immutable
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Name
Spaceship
DestinationName
Immutable
Mutable
with other objects, as they cannot be changed.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Spaceship
DestinationName
Immutable
The Destination object, in this case, we’ll assume, is Mutable.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Spaceship
DestinationName
Immutable
Mutable
Anyone who holds a reference to it -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Spaceship
DestinationName
Immutable
Mutable
can alter it.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Immutable
Mutable
This will effectively alter our "Immutable" object.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
Mutable
4. Ensure exclusive access to any mutable attributes
In other words, it would not be immutable.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Mutable
So to protect our Immutable object -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Mutable
Spaceship
DestinationName
Immutable
we have to isolate the mutable destination attribute from the outside and prevent anyone from getting hold of it.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Spaceship
DestinationName
Immutable
Destination
MutableTo achieve this, we never obtain or return a direct reference to a destination object. Instead we create a deep copy and work with that
instead.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Spaceship
DestinationName
Immutable
DestinationDestination
MutableAs long as the mutable destination object
is never directly shared,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Spaceship
DestinationName
Immutable
DestinationDestination
Mutablea change inside an external object will not have any effect on our immutable object.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
Spaceship
DestinationName
Immutable
DestinationDestination
Mutable
Okay, talk is cheap, show me the code.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return destination; }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); }
4. Ensure exclusive access to any mutable attributes
private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
To ensure exclusive access to our mutable destination attribute,
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return destination; }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
we have to check all public methods and constructors for any
incoming or outgoing Destination references.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return destination; }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
Let’s do this now.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return destination; }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
The public constructor does not receive any Destination reference.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return destination; }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
The Destination object it creates is safe, as it cannot be accessed from outside.
So the public constructor is good as it is.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return destination; }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
The public currentDestination() method returns a reference -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return destination; }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
to our internal Destination object, so this must be fixed.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
Now, instead of returning the real reference we create a deep copy
of our Destination object and return a reference to the copy instead.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
Finally, the public newDestination() method receives -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
a Destination reference -
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
and forwards this reference to our private constructor.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
Now the private constructor directly stores the mutable Destination reference it receives.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
So, by following the execution path, we found that it is directly stored in the private
constructor.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = destination; }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
This must not be allowed.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
4. Ensure exclusive access to any mutable attributes
public ImmutableSpaceship newDestination(Destination newDestination) { return new ImmutableSpaceship(this.name, newDestination); }
public Destination currentDestination() { return new Destination(destination); }
public ImmutableSpaceship(String name) { this.name = name; this.destination = new Destination("NONE"); } private ImmutableSpaceship(String name, Destination destination) { this.name = name; this.destination = new Destination(destination); }
[…]}
public final class ImmutableSpaceship { private final String name; private final Destination destination;
To stop it immediately, we store a reference to a deep copy of the external Destination object.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
• What is an Immutable?• Advantages & disadvantages• How to create an Immutable• When to use Immutables
Okay. So I've shown you what an Immutable is, I've told you about its advantages and disadvantages,
and finally, I have demonstrated how to create an Immutable class.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
There is just one last thing I would like to tell you –
When should YOU actually use an Immutable class in your code?
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
The question of whether you should design a specific class as an Immutable depends mainly on whether the performance of the
overall system is sufficient or not.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
This again depends on the nature of the system and all
aspects related to it. For example: What kind of
specific problem are you trying to solve?
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
On what kind of hardware is your program running? Is it a desktop
or web application? It also depends on the internal structure of your code.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
For example: Of how many packages, classes and methods does your code consist of? As you can see, there is no simple answer to this very complex
question.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
Each case must be looked at individually.
As a general recommendation however,
I advise you to follow this approach:
Focus on designing a system that uses
immutables to the greatest possible extent.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
Facilitate their use by designing simple
classes with few attributes and methods.
Immutables may become a burden
when they are too complex. Simplicity is key.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
In this endeavor, Clean Code and Immutables are a well matched pair, reinforcing each other.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
In the majority of cases, this approach will lead to a
system that exceeds all requirements.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
If however, testing reveals that you have not achieved
satisfactory performance, relax the immutability rules gradually.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
As much as necessary, but as little as possible.
Copyright © 2016 Marcus Bielwww.marcus-biel.com
When to use Immutables
Copyright © 2016 Marcus Biel
All rights reserved.