@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
.