org.mutabilitydetector.unittesting.matchers.reasons
Class FieldAssumptions

java.lang.Object
  extended by org.mutabilitydetector.unittesting.matchers.reasons.FieldAssumptions

public final class FieldAssumptions
extends Object

Allowed reasons for mutability warnings related to fields.

It is expected that this class will not be used directly. Instead, use the factory methods provided by AllowedReason for more fluent unit tests.

See Also:
AllowedReason.assumingFields(Iterable), AllowedReason.assumingFields(String, String...)

Method Summary
 org.hamcrest.Matcher<MutableReasonDetail> areModifiedAsPartOfAnUnobservableCachingStrategy()
          Insists that while a field may have been mutated, changes will not be observable.
 org.hamcrest.Matcher<MutableReasonDetail> areNotModifiedAndDoNotEscape()
          Insists that a mutable field is used safely.
 org.hamcrest.Matcher<MutableReasonDetail> areSafelyCopiedUnmodifiableCollectionsWithImmutableElements()
          Insists fields of collection types are copied and wrapped safely.
static FieldAssumptions named(Iterable<String> fieldNames)
          Advice: use the factory method AllowedReason.assumingFields(String, String...) for greater readability.
static FieldAssumptions named(String firstFieldName, String... otherFieldNames)
          Advice: use the factory method AllowedReason.assumingFields(String, String...) for greater readability.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

named

public static FieldAssumptions named(String firstFieldName,
                                     String... otherFieldNames)
Advice: use the factory method AllowedReason.assumingFields(String, String...) for greater readability.


named

public static FieldAssumptions named(Iterable<String> fieldNames)
Advice: use the factory method AllowedReason.assumingFields(String, String...) for greater readability.


areSafelyCopiedUnmodifiableCollectionsWithImmutableElements

public org.hamcrest.Matcher<MutableReasonDetail> areSafelyCopiedUnmodifiableCollectionsWithImmutableElements()
Insists fields of collection types are copied and wrapped safely.

One way to use mutable collection types in an immutable class is to copy the contents and wrap in an unmodifiable collection. Mutability Detector has limited support for recognising this pattern, e.g.: Collections.unmodifiableList(new ArrayList(original));. However, the methods used for copying and wrapping must be those available in the JDK. If you are using your own, or third-party collection types, Mutability Detector will raise a warning. This allowed reason will permit those warnings.

Example usage:

 
 import com.google.common.collect.Lists;
 
 @Immutable
 public final class SafelyCopiesAndWraps {
   private final List<String> unmodifiableCopy;
   
   public SafelyCopiesAndWraps(List original) {
     // use Guava method to copy 
     this.unmodifiableCopy = Collections.unmodifiableList(Lists.newArrayList(original));
   }
   
   // ... other methods
 }
 
  // a warning will be raised because copy method, Guava's Lists.newArrayList(),  is unrecognised
  assertInstancesOf(SafelyCopiesAndWraps.class, areImmutable());
  
  // use FieldAssumptions to insist the usage is safe
  assertInstancesOf(SafelyCopiesAndWraps.class, 
                    areImmutable(),
                    assumingFields("unmodifiableCopy").areSafelyCopiedUnmodifiableCollectionsWithImmutableElements());
 
 

This case will also work when the collection is declared (with generics) to contain a mutable type.

See Also:
MutabilityReason.ABSTRACT_COLLECTION_TYPE_TO_FIELD, MutabilityReason.ABSTRACT_TYPE_TO_FIELD, MutabilityReason.COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE

areNotModifiedAndDoNotEscape

public org.hamcrest.Matcher<MutableReasonDetail> areNotModifiedAndDoNotEscape()
Insists that a mutable field is used safely.

A requirement for immutability is "If the instance fields include references to mutable objects, don't allow those objects to be changed" [0]. This necessitates that any mutable fields are not modified (e.g. by calling a method which mutates it) and their reference is not published (where client code could invoke a mutating method). While greater care is needed, it is possible to create immutable objects composed of mutable fields.

Example usage:

 
 import java.util.Date;
 
 @Immutable
 public final class UsesMutableField {
   private final Date myDate;
   
   public UsesMutableField(Date original) {
     this.myDate = new Date(original.getTime());
   }
   
   public Date getDate() {
     // if we used 'return myDate;' we would be publishing reference
     return new Date(myDate.getTime());
   }
   
   // ... other methods, which never call myDate.setTime()
   // if we called, e.g. setTime() we would be mutating the field
 }
 
  // a warning will be raised because myDate is of a mutable type, java.util.Date
  assertInstancesOf(UsesMutableField.class, areImmutable());
  
  // use FieldAssumptions to insist the usage is safe
  assertInstancesOf(UsesMutableField.class, 
                    areImmutable(),
                    assumingFields("myDate").areNotModifiedAndDoNotEscape());
 
 

Note: this allowed reason also assumes the defensive copy of original into myDate, although there is currently no support for automatically detecting this.

[0] A Strategy for Defining Immutable Objects

See Also:
MutabilityReason.MUTABLE_TYPE_TO_FIELD, MutabilityReason.COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE, MutabilityReason.ARRAY_TYPE_INHERENTLY_MUTABLE

areModifiedAsPartOfAnUnobservableCachingStrategy

public org.hamcrest.Matcher<MutableReasonDetail> areModifiedAsPartOfAnUnobservableCachingStrategy()
Insists that while a field may have been mutated, changes will not be observable.

As described in the documentation for Java Concurrency In Practice's @Immutable annotation, objects may maintain mutable state, as long as the mutation is internal, and cannot be observed by callers. This can be useful for providing caching within the object instance. A classic example of this is the Open JDK's implementation of String. Each instance uses the hash field to cache the result of Object.hashCode(). The hash field is computed lazily, and the field is reassigned (a mutation), however clients of String can not observe the mutation as it is internal.

While this technique is tricky, it can be very useful for performance reasons. Unfortunately, Mutability Detector cannot tell the difference between: lazily storing the result of a computation for future lookup; and a setter method.

This allowed reason will permit mutable fields, and also reassigning field references.

Example usage:

 
 import java.util.Date;
 
 @Immutable
 public static final class ReassignsHashCode {
   private final String name;
   private final Integer age;
   private int hash;
     
   public ReassignsHashCode(String name, Integer age) {
     this.name = name;
     this.age = age;
   }
     
   @Override
   public int hashCode() {
     if (hash == 0) {
         hash = name.hashCode() + age.hashCode();
     }
     return hash;
   }
 }
 
  // a warning will be raised because the hash field is reassigned, as with a setter method
  assertInstancesOf(ReassignsHashCode.class, areImmutable());
  
  // use FieldAssumptions to insist the usage is safe
  assertInstancesOf(ReassignsHashCode.class, 
                    areImmutable(),
                    assumingFields("hash").areModifiedAsPartOfAnUnobservableCachingStrategy());
 
 

See Also:
MutabilityReason.MUTABLE_TYPE_TO_FIELD, MutabilityReason.COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE, MutabilityReason.ARRAY_TYPE_INHERENTLY_MUTABLE, MutabilityReason.FIELD_CAN_BE_REASSIGNED, MutabilityReason.NON_FINAL_FIELD


Copyright © 2013. All Rights Reserved.