Package groovy.transform

# Annotation Type Immutable

@Documented @Retention(RUNTIME) @Target(TYPE) public @interface Immutable
Class annotation used to assist in the creation of immutable classes.

It allows you to write classes in this shortened form:

     @groovy.transform.Immutable class Customer {
         String first, last
         int age
         Date since
         Collection favItems
     }
     def d = new Date()
     def c1 = new Customer(first:'Tom', last:'Jones', age:21, since:d, favItems:['Books', 'Games'])
     def c2 = new Customer('Tom', 'Jones', 21, d, ['Books', 'Games'])
     assert c1 == c2

The `@Immutable` annotation instructs the compiler to execute an AST transformation which adds the necessary getters, constructors, equals, hashCode and other helper methods that are typically written when creating immutable classes with the defined properties.

A class created in this way has the following characteristics:

- The class is automatically made final.
- Properties must be of an immutable type or a type with a strategy for handling non-immutable characteristics. Specifically, the type must be one of the primitive or wrapper types, Strings, enums, other `@Immutable` classes or known immutables (e.g. java.awt.Color, java.net.URI, java.util.UUID). Also handled are Cloneable classes, collections, maps and arrays, and other "effectively immutable" classes with special handling (e.g. java.util.Date).
- Properties automatically have private, final backing fields with getters. Attempts to update the property will result in a `ReadOnlyPropertyException`.
- A map-based constructor is provided which allows you to set properties by name.
- A tuple-style constructor is provided which allows you to set properties in the same order as they are defined.
- Default `equals`, `hashCode` and `toString` methods are provided based on the property values. Though not normally required, you may write your own implementations of these methods. For `equals` and `hashCode`, if you do write your own method, it is up to you to obey the general contract for `equals` methods and supply a corresponding matching `hashCode` method. If you do provide one of these methods explicitly, the default implementation will be made available in a private "underscore" variant which you can call. E.g., you could provide a (not very elegant) multi-line formatted `toString` method for `Customer` above as follows:

           String toString() {
              _toString().replaceAll(/\(/, '(\n\t').replaceAll(/\)/, '\n)').replaceAll(/, /, '\n\t')
          }

  If an "underscore" version of the respective method already exists, then no default implementation is provided.
- `Date`s, `Cloneable`s and arrays are defensively copied on the way in (constructor) and out (getters). Arrays and `Cloneable` objects use the `clone` method. For your own classes, it is up to you to define this method and use deep cloning if appropriate.
- `Collection`s and `Map`s are wrapped by immutable wrapper classes (but not deeply cloned!). Attempts to update them will result in an `UnsupportedOperationException`.
- Fields that are enums or other `@Immutable` classes are allowed but for an otherwise possible mutable property type, an error is thrown.
- You don't have to follow Groovy's normal property conventions, e.g. you can create an explicit private field and then you can write explicit get and set methods. Such an approach, isn't currently prohibited (to give you some wiggle room to get around these conventions) but any fields created in this way are deemed not to be part of the significant state of the object and aren't factored into the `equals` or `hashCode` methods. Similarly, you may use static properties (though usually this is discouraged) and these too will be ignored as far as significant state is concerned. If you do break standard conventions, you do so at your own risk and your objects may no longer be immutable. It is up to you to ensure that your objects remain immutable at least to the extent expected in other parts of your program!

Immutable classes are particularly useful for functional and concurrent styles of programming and for use as key values within maps. If you want similar functionality to what this annotation provides but don't need immutability then consider using `@Canonical`.

Customising behaviour:

You can customise the toString() method provided for you by `@Immutable` by also adding the `@ToString` annotation to your class definition.

Limitations:

- As outlined above, Arrays and `Cloneable` objects use the `clone` method. For your own classes, it is up to you to define this method and use deep cloning if appropriate.
- As outlined above, `Collection`s and `Map`s are wrapped by immutable wrapper classes (but not deeply cloned!).
- Currently `BigInteger` and `BigDecimal` are deemed immutable but see: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6348370
- `java.awt.Color` is treated as "effectively immutable" but is not final so while not normally used with child classes, it isn't strictly immutable. Use at your own risk.
- `java.util.Date` is treated as "effectively immutable" but is not final so it isn't strictly immutable. Use at your own risk.
- Groovy's normal map-style naming conventions will not be available if the first property has type `LinkedHashMap` or if there is a single Map, AbstractMap or HashMap property.

More examples:

--------------------------------------------------------------------------------

     import groovy.transform.*

     @Canonical
     class Building {
         String name
         int floors
         boolean officeSpace
     }

     // Constructors are added.
     def officeSpace = new Building('Initech office', 1, true)

     // toString() added.
     assert officeSpace.toString() == 'Building(Initech office, 1, true)'

     // Default values are used if constructor
     // arguments are not assigned.
     def theOffice = new Building('Wernham Hogg Paper Company')
     assert theOffice.floors == 0
     theOffice.officeSpace = true

     def anotherOfficeSpace = new Building(name: 'Initech office', floors: 1, officeSpace: true)

     // equals() method is added.
     assert anotherOfficeSpace == officeSpace

     // equals() and hashCode() are added, so duplicate is not in Set.
     def offices = [officeSpace, anotherOfficeSpace, theOffice] as Set
     assert offices.size() == 2
     assert offices.name.join(',') == 'Initech office,Wernham Hogg Paper Company'

     @Canonical
     @ToString(excludes='age')  // Customize one of the transformations.
     class Person {
         String name
         int age
     }

     def mrhaki = new Person('mrhaki', 37)
     assert mrhaki.toString() == 'Person(mrhaki)'

Since:
  1.7
Author:
  Paul King, Andre Steingress
See Also:
  - `ToString`
  - `Canonical`

## Optional Element Summary

Optional Elements
Modifier and Type
Optional Element
Description
`boolean`
`copyWith`
If `true`, this adds a method `copyWith` which takes a Map of new property values and returns a new instance of the Immutable class with these values set.
`Class[]`
`knownImmutableClasses`
Allows you to provide `@Immutable` with a list of classes which are deemed immutable.
`String[]`
`knownImmutables`
Allows you to provide `@Immutable` with a list of property names which are deemed immutable.

## Element Details

### knownImmutableClasses

Class[] knownImmutableClasses
Allows you to provide `@Immutable` with a list of classes which are deemed immutable. By supplying a class in this list, you are vouching for its immutability and `@Immutable` will do no further checks. Example:

     import groovy.transform.*
     @Immutable(knownImmutableClasses = [Address])
     class Person {
         String first, last
         Address address
     }

     @TupleConstructor
     class Address {
         final String street
     }

Since:
  1.8.7

Default:
  `{}`

### knownImmutables

String[] knownImmutables
Allows you to provide `@Immutable` with a list of property names which are deemed immutable. By supplying a property's name in this list, you are vouching for its immutability and `@Immutable` will do no further checks. Example:

     @groovy.transform.Immutable(knownImmutables = ['address'])
     class Person {
         String first, last
         Address address
     }
     ...

Since:
  2.1.0

Default:
  `{}`

### copyWith

boolean copyWith
If `true`, this adds a method `copyWith` which takes a Map of new property values and returns a new instance of the Immutable class with these values set. Example:

     @groovy.transform.Immutable(copyWith = true)
     class Person {
         String first, last
     }

     def tim   = new Person( 'tim', 'yates' )
     def alice = tim.copyWith( first:'alice' )

     assert tim.first   == 'tim'
     assert alice.first == 'alice'

Unknown keys in the map are ignored, and if the values would not change the object, then the original object is returned. If a method called `copyWith` that takes a single parameter already exists in the class, then this setting is ignored, and no method is generated.

Since:
  2.2.0

Default:
  `false`