@Documented @Retention(value=RUNTIME) @Target(value={TYPE,FIELD,CONSTRUCTOR,METHOD,PARAMETER}) public @interface Immutable
Instances of the type (class or interface) to which this annotation is applied are immutable. This means that its state cannot be seen to change by callers, which implies that
final,Vouch annotation can be used,
especially annotation of @Vouch("Immutable") on a
field, to suppress overly conservative tool results.
This annotation is currently verified, but not defined, by
restricting how the fields of the class are declared and annotated. This is a
conservative, although easily understood, way to verify the Immutable
assertion. Specifically, for a class annotated as
@Immutable to be verified, each field must be
final; and
@Immutable.
Immutable objects are inherently thread-safe; they may be passed between
threads or published without synchronization. Therefore instances of an
Immutable type are also ThreadSafe, but not necessarily vice
versa.
Typically, subtypes of the annotated type must be explicitly annotated
@Immutable as well. It is a modeling error if they are not.
This annotation has two attributes, implementationOnly and
verify, that control how subtypes of an Immutable type must
be annotated. The implementationOnly attribute indicates that the
implementation of the annotated class should be assured without making a
general statement about the visible behavior of the type or its subtypes.
There are several rules with regards to the implementationOnly
attribute on Immutable types:
false when
Immutable appears on an interface, because interfaces do not have an
implementation.Immutable must
be annotated with Immutable; classes that implement an interface
annotated with Immutable must be annotated with
@Immutable(implementationOnly=false).Immutable, but that is not verifiable using the syntactic and
annotation constraints described above. For this case, we provide the
"escape hatch" of turning off tool verification for the annotation with the
verify attribute. For example,
@Immutable(verify=false) would skip tool verification entirely.
A type may not be annotated with both @Immutable and
@Mutable.
An annotation type declaration may be annotated with @Immutable
so that any static fields in the type are verified as immutable.
By default the static part of a class implementation is checked that it is immutable as well as the instance part. It is possible to specify which portions of the class implementation are checked using the appliesTo attribute. This value can be one of
@ThreadSafeThread safety and immutability are two points along the same axis. This set of annotations can actually describe three points along the axis:
@Mutable and @NotThreadSafe
@Mutable
, or just @NotThreadSafe. The type contains mutable state
that is not safe to access concurrently from multiple threads.
@Mutable and @ThreadSafe
@ThreadSafe. The type contains
mutable state that is safe to access concurrently from multiple threads.
@Immutable and @ThreadSafe
@Immutable. The type contains no
mutable state, and is thus safe to access concurrently from multiple threads.
The combination @Immutable and
@NotThreadSafe is a modeling error because an immutable
type is obviously thread safe.
An @Immutable interface may extend a
@ThreadSafe interface. An @Immutable
class may implement a @ThreadSafe interface.
A @ThreadSafe class may extend a class annotated with
@Immutable(implementationOnly=true).
Point class below is considered thread-safe.
@Immutable
public class Point {
final int f_x;
final int f_y;
@Unique("return")
@RegionEffects("none")
public Point(int x, int y) {
f_x = x;
f_y = y;
}
@RegionEffects("reads Instance")
public int getX() {
return f_x;
}
@RegionEffects("reads Instance")
public int getY() {
return f_y;
}
}
A @Vouch("Immutable") annotation is used to vouch
that a private array is immutable after object construction. Because the Java
language does not allow the programmer to express that the contents of the
array are unchanging, use of a Vouch is necessary in this example.
@Immutable
public class Aircraft {
@Vouch("Immutable")
private final Wing[] f_wings;
@Unique("return")
@RegionEffects("none")
public Aircraft() {
f_wings = new Wing[2];
f_wings[0] = new Wing();
f_wings[1] = new Wing();
}
...
}
In the example below a @Vouch("Immutable") annotation
is used to vouch that a parameterized type references an immutable object.
@Immutable
public class Frame<T> {
@Vouch("Immutable")
private final T value;
private final double timeFraction;
...
}
It is possible to avoid the need to annotate the value field
@Vouch("Immutable") if the parameterized type
T extends a type annotated to be immutable. For example, if there
exists a type Base that is annotated @Immutable, then
the listing above could be modified to avoid the need for a vouch.
@Immutable
public class Frame<T extends Base> {
private final T value;
private final double timeFraction;
...
}
One area where the @Vouch("Immutable") annotation is
often needed is when collections are used within an immutable class. The code
below shows an example where a collection is vouched for.
@Immutable
public final class EntityMgr {
@Vouch("Immutable")
private final Map<String, String> f_known;
public EntityMgr(Map<String, String> defs) {
Map<String, String> known = new HashMap<String, String>();
// add defaults
known.put("<", "<");
known.put(">", ">");
known.putAll(defs);
f_known = Collections.unmodifiableMap(known);
}
public EntityMgr() {
this(Collections.<String, String> emptyMap());
}
public String convertIfRecognized(String entity) {
String value = f_known.get(entity);
return value == null ? entity : value;
}
}
As an example of treating the static and instance state of a class
differently, consider the Point class below. The instances of
the class are considered immutable, but the static state is mutable
and thread-safe. This is because the static state is used to maintain a
cache of instantiated points.
@Immutable(appliesTo=Part.Instance)
@ThreadSafe(appliesTo=Part.Static)
@Region("private static Points")
@RegionLock("PointsLock is points protects Points")
public final class Point {
@UniqueInRegion("Points")
private final static List<Point> points = new ArrayList<Point>();
private final int x;
private final int y;
private Point(final int x, final int y) {
this.x = x;
this.y = y;
}
public boolean equals(final int x, final int y) {
return this.x == x && this.y == y;
}
public static Point getPoint(final int x, final int y) {
synchronized (points) {
for (final Point p : points) {
if (p.equals(x, y)) return p;
}
final Point p = new Point(x, y);
points.add(p);
return p;
}
}
// ...
}
An immutable reference cannot be used to modify the state of the referenced
object. Furthermore, no other references to the referenced object may be used
to modify the state of the object either. The state of the object will never
change. In contrast, a ReadOnly reference may not be used to modify
the state of the referenced object, but the object may have other non-read
only aliases that can be used to modify the state of the object.
Immutable references are not currently checked by analysis.
@annotate tag.
/**
* @annotate Immutable
*/
public class Point {
...
}
/** * @annotate Immutable */ public class KeyTimes implements Iterable{ /** * @annotate Vouch("Immutable") */ private final List<Double> f_keyTimes; ... }
/**
* @annotate Immutable
*/
public class Aircraft {
/**
* @annotate Vouch("Immutable")
*/
private final Wing[] f_wings;
...
}
Implementation note: This annotation is derived from
@Immutable proposed by Brian Goetz and Tim Peierls in the
book Java Concurrency in Practice (Addison-Wesley 2006) we have simply
adapted it to have semantics as a promise. Further, the annotation in
net.jcip.annotations may be used instead of this one with the same
tool behavior. One difference between the two annotations is that the
annotation in com.surelogic adds the implementationOnly and
verify attributes—these attributes can not be changed from
their default values if the the net.jcip.annotations annotation is
used.AnnotationBounds,
ThreadSafe,
Mutable,
Part,
Vouch,
ReadOnly| Modifier and Type | Optional Element and Description |
|---|---|
Part |
appliesTo
Indicates whether the instance state of the class, static state of the
class, or both are subject to verification.
|
boolean |
implementationOnly
Indicates that the implementation of the annotated class should be assured
without making a general statement about the visible behavior of the type
or its subtypes.
|
String |
value
When annotating a constructor or a method, this attribute must be
"this" or "return" to clarify the intent that it is the
receiver that is immutable. |
boolean |
verify
Indicates whether or not tool verification should be attempted.
|
public abstract boolean implementationOnly
true if only the annotated class should be assured without
making a general statement about the visible behavior of the type
or its subtypes, false otherwise. The default value for
this attribute is false.public abstract boolean verify
true if the claim should be verified by a tool, such as
SureLogic JSure, false otherwise. The default value for
this attribute is true.public abstract Part appliesTo
public abstract String value
"this" or "return" to clarify the intent that it is the
receiver that is immutable. It is a modeling error if the attribute is not
"this" or "return" in this case. When annotated on a
constructor "this" or "return" have equivalent semantics.
class C {
@Immutable("this")
public C() { ... }
@Immutable("return")
void method() { ... }
...
}
This attribute is only used when annotating a constructor or method; it is
a modeling error for the value to be anything other than the default when
annotating a type or a parameter.
int proc(@Immutable int value /* Illegal: Parameter has primitive type */,
@Immutable Object settings /* Legal: Parameter has reference type */) { ... }
The value of this attribute must conform to the following grammar (in Augmented Backus–Naur
Form):
value = ["this" / "return"] ; See above comments
Copyright © 2012 Surelogic, Inc.. All Rights Reserved.