Immutable transference is the only safe way to share state. If a state structure is immutable, then it is impossible for anyone to alter it. In Akka, this is how we guarantee the message we send is the same message our recipient receives. By making our messages immutable, we assure their accuracy and eliminate the possibility of contention. Let’s take a look at two ways we can model messages in Java.
Immutable Message
ImmutableHello
Message in Java
public final class ImmutableHello {
private final String name;
private ImmutableHello() {}
public ImmutableHello(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
- The class is marked
final
so it cannot be extended. - Prefix the class with
Immutable
, this is the convention, not required but good practice. - Class variable marked
private final
, enforcing local-only access and immutability. private
no-arg constructor prevents instantiation without a class variable.- Class constructor requires class variable for creation.
- Getters only.
Alternative
There is another way to model a Java class to enforce immutability, but some regard it with disdain as it does not follow accepted practices for Java class design. Personally, I like it as it is succinct.
ImmutableHello
Message in Java (Alternative)
public final class ImmutableHello {
public final String name;
private ImmutableHello() {}
public ImmutableHello(String name) {
this.name = name;
}
}
- The class is marked
final
so it cannot be extended. - Prefix the class with
Immutable
, this is the convention, not required but good practice. - Class variable marked
final
, enforcing immutability. private
no-arg constructor prevents instantiation without a class variable.- Class constructor requires class variable for creation.
equals, toString and hashCode
The other issue one must consider when modeling immutable classes in Java for messaging is equality. We might desire to compare the message received to another for validation purposes or what have you. As a result, equals
, hashcode
, and toString
implementations are in order.
@Override
public String toString(){
return "ImmutableHello{name=" + name + "}";
}
@Override
public boolean equals(Object o){
if (o == this) return true;
if (o instanceof ImmutableHello) {
ImmutableHello that = (ImmutableHello) o;
return (this.name.equals(that.name));
}
return false;
}
@Override
public int hashCode(){
int h = 1;
h *= 1000003;
h ^= name.hashCode();
return h;
}