Organize how to use Java's org.Immutables.
The official website is below. (Basically, the content of this article is based on this) https://immutables.github.io/
Set dependency in pom.xml of maven.
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>2.8.2</version>
<scope>provided</scope>
</dependency>
Create an Immutable object by creating a Value class that stores data and building an automatically generated Immutable class based on it. Used when creating an object that you do not want to change.
Value --Declare with Immutable annotation. --Interface or class is OK. --Immutable class is automatically generated when compiled. --The class name of the Immutable class is ImmutableXxx. (Xxx is the class name of the Value class) --The automatically generated Immutable class should be created in target / generated-sources / annotations by default when compiled with maven compile.
import java.util.List;
import org.immutables.value.Value.Immutable;
@Immutable
public interface User {
String name();
String address();
int age();
List<String> hobbies();
}
Builder --Use the Immutable class automatically generated from the Value class (User class in the above example). --Call the builder method of the Immutable class, assign the data, and finally call the build method. --This will create an Immutable object. --Data can be obtained by the method of Value class.
User user = ImmutableUser.builder()
.name("Tom")
.age(21)
.address("Tokyo, Japan")
.addHobbies("reading", "skiing")
.build();
System.out.println(user.toString());
// User{name=Tom, address=Tokyo, Japan, age=21, hobbies=[reading, skiing]}
System.out.println(user.name());
// Tom
System.out.println(user.age());
// 21
--You cannot add data to Immutable objects. (UnsupportedOperationException occurs when trying to add data to List class or Map class)
//listMap is an Immutable object
listMap.list().add("item"); // java.lang.UnsupportedOperationException
listMap.map().put("key", "value"); //java.lang.UnsupportedOperationException
from() --Use the from method to create another Immutable object based on one Immutable object.
User userTemp = ImmutableUser.builder()
.name("Tom")
.age(21)
.address("Tokyo, Japan")
.addHobbies("reading", "skiing")
.build();
User user = ImmutableUser.builder()
.from(userTemp)
.address("Osaka, Japan")
.build();
System.out.println(user.toString());
// User{name=Tom, address=Osaka, Japan, age=21, hobbies=[reading, skiing]}
Constructor --When creating an Immutable object with the constructor, add Parameter annotation to the method.
import org.immutables.value.Value.Immutable;
import org.immutables.value.Value.Parameter;
@Immutable
public interface Item {
@Parameter
String name();
@Parameter
int price();
}
--Create an Immutable object by giving data to the of method of the Immutable class.
Item item = ImmutableItem.of("apple", 213);
System.out.println(item);
// Item{name=apple, price=213}
Optional
--Use Optional when defining an optional item in the Immutable class.
import java.util.Optional;
import org.immutables.value.Value.Immutable;
@Immutable
public interface Address {
String country();
String city();
String blockNmuber();
Optional<String> apartmentName();
}
--Items defined in Optional do not need to be set with data, but items that are not set with IllegalStateException will occur if data is not set.
Address address1=ImmutableAddress.builder()
.country("Japan")
.city("Yokohama")
.blockNmuber("1-2-3")
.build(); // OK
Address address2=ImmutableAddress.builder()
.country("Japan")
.city("Chiba")
.blockNmuber("1-2-3")
.apartmentName("YYY Apartment")
.build(); // OK
Address address3=ImmutableAddress.builder()
.country("Japan")
.city("Chiba")
.build(); // java.lang.IllegalStateException: Cannot build Address, some of required attributes are not set [blockNmuber]
Default
--Add the Default annotation to the item that you want to have the default value, and define the default method.
import org.immutables.value.Value.Default;
import org.immutables.value.Value.Immutable;
@Immutable
public interface AddressWithDefault {
@Default
default String country(){
return "Japan";
}
String city();
String blockNmuber();
}
--If you do not set data when creating an Immutable object, the default value will be set.
AddressWithDefault address = ImmutableAddressWithDefault.builder()
.city("Saitama")
.blockNmuber("4-5-6")
.build();
System.out.println(address.toString());
// AddressWithDefault{country=Japan, city=Saitama, blockNmuber=4-5-6}
Derived
--Add Derived annotation and define the item to be processed based on the data set in Immutable object.
import java.util.Map;
import org.immutables.value.Value.Derived;
import org.immutables.value.Value.Immutable;
@Immutable
public abstract class Receipt {
abstract Map<String, Integer> items();
@Derived
public int total() {
//Sum the values of items
return items().values().stream().reduce(0, Integer::sum);
}
}
Receipt receipt = ImmutableReceipt.builder()
.putItems("bread", 210)
.putItems("milk", 100)
.build();
System.out.println(receipt.total());
// 310
Precondition check
--Define the check process when the Immutable object is created with the Check annotation.
import java.util.List;
import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;
@Immutable
public abstract class Cart {
abstract List<String> items();
@Check
protected void check() {
if (items() == null || items().isEmpty()) {
throw new IllegalStateException();
}
}
}
--If the check conditions are not met, an error can be generated when creating an Immutable object.
ImmutableCart.builder().build(); // java.lang.IllegalStateException
Wrapper Types
--If you have a Wrapped interface and a Wrapper class, the definition of the Value class will be simple and you will not have to write similar code many times.
--Wrapped interface
@Value.Style(
typeAbstract = "_*",
typeImmutable = "*",
visibility = ImplementationVisibility.PUBLIC,
defaults = @Value.Immutable(builder = false, copy = false))
public @interface Wrapped {
}
--Wrapper class
public static abstract class Wrapper<T> {
@Value.Parameter
public abstract T value();
@Override
public String toString() {
return getClass().getSimpleName() + "(" + value() + ")";
}
}
--The Value class and how to call it are as follows.
@Value.Immutable
@Wrapped
public static abstract class _FirstName extends Wrapper<String> {
}
@Value.Immutable
@Wrapped
public static abstract class _LastName extends Wrapper<String> {
}
@Value.Immutable
@Wrapped
public static abstract class _Age extends Wrapper<Integer> {
}
FirstName firstName = FirstName.of("Taro");
LastName lastName = LastName.of("Yamada");
Age age = Age.of(45);
--Clean and compile like mvn clean compile
.
--Set false in the useIncrementalCompilation option.
- https://immutables.github.io/getstarted.html#troubleshooting
Recommended Posts