@SubtypeOf(value={}) @DefaultFor(value={LOCAL_VARIABLE,RESOURCE_VARIABLE}) @Documented @Retention(value=RUNTIME) @Target(value={TYPE_USE,TYPE_PARAMETER}) public @interface Raw
NullnessRawnessChecker
uses rawness to track
initialization of NonNull
fields.
This type qualifier indicates that the object might not have been fully initialized. An object
is fully initialized when each of its fields contains a value that satisfies its type qualifier.
What type qualifiers are considered depends on the checker; for instance, the NullnessRawnessChecker
considers NonNull
.
Therefore, reading a field of an object of type Raw
might yield a value that does not
correspond to the declared type qualifier for that field. For instance, in the NullnessRawnessChecker
, a field might be null
even
if it has been annotated as NonNull
.
More precisely, an expression of type @Raw(T.class)
refers to an object that has all
fields of T
(and any super-classes) initialized (e.g., to a non-null value in the NullnessRawnessChecker
). Just @Raw
is equivalent
to @Raw(Object.class)
.
At the beginning of a constructor, the fields of the object are not yet initialized and thus
Raw(supertype)
is used as the type of the self-reference this
.
Consider a class B
that is a subtype of A
. At the beginning of the constructor of
B
, this
has the type @Raw(A.class)
, since all fields of A
have
been initialized by the super-constructor. If during the constructor also all fields of B
are initialized, then the type of this
changes to @Raw(B.class)
(and otherwise,
if not all fields are initialized, an error is issued).
At the end of the constructor, the type is not fully initialized. Rather, it is Raw(<em>supertype</em>)
.
Note that it would not be sound to type this
as NonRaw
anywhere in a
constructor (with the exception of final classes; but this is currently not implemented), because
there might be subclasses with uninitialized fields. The following example shows why:
class A {
@NonNull String a;
public A() {
a = "";
// Now, all fields of A are initialized.
// However, if this constructor is invoked as part of 'new B()', then
// the fields of B are not yet initialized.
// If we would type 'this' as @NonRaw, then the following call is valid:
foo();
}
void foo() {}
}
class B extends A {
@NonNull String b;
@Override
void foo() {
// Dereferencing 'b' is ok, since 'this' is @NonRaw and 'b' @NonNull.
// However, when executing 'new B()', this line throws a null-pointer exception.
b.toString();
}
}
public abstract Class<?> value
@Raw(T.class)
has all type-frames
initialized starting at Object
down to (and including) T
.