To start using Immutables, all you need to do is to add it to your dependencies. For example with Maven project you will have:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dependency> | |
<groupId>org.immutables</groupId> | |
<artifactId>value</artifactId> | |
<version>${immutables-value.version}</version> | |
<scope>provided</scope> | |
</dependency> |
IDE configuration
Let's see what should be done in IntelliJ IDEA to make work with Immutables easier. First we need to make sure that Annotation Processors are enabled for your project. To do this go to File > Settings > Build, Execution, Deployment > Compiler > Annotation Processors and select your module/project where you want to enable annotation processing:"Enable annotation processing" checkbox should be checked together with "Obtain annotation processors from project classpath" option (like on the screenshot above). If they are not selected - click them and save the config. After this generated classes will appear in target subfolder. To make them visible to you in the IDE open module settings:
Select your module, and make sure that folder that contains your generated code is marked as "Sources" and "Exclude" is removed from that folder (in our case it's target/generated-sources/annotations). If you don't do this - your project will compile and work fine with Maven but not in the IDE, as generated stuff will not be visible to it.
For Eclipse configuration you can see the documentation here.
Creating value objects
Finally! Let's start creating immutable value objects. For purposes of this post we will consider a Book value object example. We will have a Book written by the Author and identified by ISBN number. Let's start with the Author:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Value.Immutable | |
public abstract class ISBN { | |
public abstract String getNumber(); | |
@Value.Check | |
protected void check() { | |
Preconditions.checkState( getNumber() != null, | |
"'ISBN number' cannot be null" | |
); | |
Preconditions.checkState( getNumber().length() >= 10, | |
"'ISBN number' should have at least ten chars" | |
); | |
Preconditions.checkState( isValidISBN10( getNumber() ), | |
"'ISBN number' should be a valid ISBN number" | |
); | |
} | |
private static boolean isValidISBN10(final String isbn) { | |
String digits = isbn.replaceAll( "[^\\d]", "" ); | |
if ( digits.length() != 10 ) { | |
return false; | |
} | |
int sum = 0; | |
for ( int i = 0; i < digits.length() - 1; i++ ) { | |
sum += ( digits.charAt( i ) - '0' ) * ( i + 1 ); | |
} | |
char checkSum = digits.charAt( 9 ); | |
return sum % 11 == ( checkSum == 'X' ? 10 : checkSum - '0' ); | |
} | |
} |
Now let's combine our ISBN and Author into a Book and perform some simple manipulations on the book instance.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Value.Immutable | |
public interface Book { | |
ISBN getISBN(); | |
String getTitle(); | |
Author getAuthor(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BookTest { | |
@Test | |
public void bookManipulations() throws Exception { | |
Book firstBook = ImmutableBook.builder() | |
.title( "Immutables in Java" ) | |
.iSBN( ImmutableISBN.builder().number( "5-02-013850-9" ).build() ) | |
.author( | |
ImmutableAuthor.builder() | |
.name( "John" ) | |
.surname( "Smith" ) | |
.build() | |
).build(); | |
// creates a new book with same author and ISBN but new title | |
Book secondBook = ( (ImmutableBook) firstBook ).withTitle( "Immutables in Java. Second edition" ); | |
// as the title is changed the books are different | |
assertThat( firstBook ).isNotEqualTo( secondBook ); | |
// but author and ISBN are still equals | |
assertThat( firstBook.getAuthor() ).isEqualTo( secondBook.getAuthor() ); | |
assertThat( firstBook.getISBN() ).isEqualTo( secondBook.getISBN() ); | |
// but if you try to use the same value - you'll receive the same object: | |
assertThat( firstBook ).isEqualTo( ( (ImmutableBook) firstBook ).withTitle( "Immutables in Java" ) ); | |
} | |
} |
All the code from this post can be found on GitHub.
To be continued...
COMMENTS