Table of Contents
&&
||
? :
Much of the work in a program is done by evaluating expressions, either for their side effects, such as assignments to variables, or for their values, which can be used as arguments or operands in larger expressions, or to affect the execution sequence in statements, or both.
This chapter specifies the meanings of expressions and the rules for their evaluation.
When an expression in a program is evaluated (executed), the result denotes one of three things:
Evaluation of an expression can also produce side effects, because expressions may contain embedded assignments, increment operators, decrement operators, and method invocations.
An expression denotes nothing
if and only if it is a method invocation (§15.12)
that invokes a method that does not return a value, that is, a method
declared void
(§8.4). Such an expression can be
used only as an expression statement (§14.8),
because every other context in which an expression can appear requires
the expression to denote something. An expression statement that is a
method invocation may also invoke a method that produces a result; in
this case the value returned by the method is quietly
discarded.
Value set conversion (§5.1.13) is applied to the result of every expression that produces a value.
Each expression occurs in either:
The declaration of some (class or interface) type that is being declared: in a field initializer, in a static initializer, in an instance initializer, in a constructor declaration, in an annotation, or in the code for a method.
An annotation (§9.7) of a package or of a top level type declaration.
If an expression denotes a variable, and a value is required for use in further evaluation, then the value of that variable is used. In this context, if the expression denotes a variable or a value, we may speak simply of the value of the expression.
If the value of a variable of
type float
or double
is used in this manner, then value set
conversion (§5.1.13) is applied to the value of
the variable.
If an expression denotes a variable or a value, then the expression has a type known at compile time. The rules for determining the type of an expression are explained separately below for each kind of expression.
The value of an expression is assignment compatible (§5.2) with the type of the expression, unless heap pollution (§4.12.2) occurs.
Likewise, the value stored in a variable is always compatible with the type of the variable, unless heap pollution occurs.
In other words, the value of an expression whose type is T is always suitable for assignment to a variable of type T.
Note that an expression whose
type is a class type F that is declared final
is guaranteed to
have a value that is either a null reference or an object whose class
is F itself, because final
types have no subclasses.
If the type of an expression
is float
or double
, then there is a question as to what value set
(§4.2.3) the value of the expression is drawn
from. This is governed by the rules of value set conversion
(§5.1.13); these rules in turn depend on whether
or not the expression is FP-strict.
Every compile-time constant expression (§15.28) is FP-strict.
If an expression is not a
compile-time constant expression, then consider all the class
declarations, interface declarations, and method declarations that
contain the expression. If any such declaration
bears the strictfp
modifier (§8.1.1.3,
§8.4.3.5, §9.1.1.2), then
the expression is FP-strict.
If a class, interface, or
method, X, is declared strictfp
, then X and any class,
interface, method, constructor, instance initializer, static
initializer or variable initializer within X is said to
be FP-strict.
Note that an annotation (§9.7) element value (§9.6) is always FP-strict, because it is always a compile-time constant expression.
It follows that an expression
is not FP-strict if and only if it is not a compile-time constant
expression and it does not appear within any
declaration that has the strictfp
modifier.
Within an FP-strict expression, all intermediate values must be elements of the float value set or the double value set, implying that the results of all FP-strict expressions must be those predicted by IEEE 754 arithmetic on operands represented using single and double formats.
Within an expression that is not FP-strict, some leeway is granted for an implementation to use an extended exponent range to represent intermediate results; the net effect, roughly speaking, is that a calculation might produce "the correct answer" in situations where exclusive use of the float value set or double value set might result in overflow or underflow.
If the type of an expression is a primitive type, then the value of the expression is of that same primitive type.
If the type of an expression
is a reference type, then the class of the referenced object, or even
whether the value is a reference to an object rather than null
, is
not necessarily known at compile time. There are a few places in the
Java programming language where the actual class of a referenced object affects
program execution in a manner that cannot be deduced from the type of
the expression. They are as follows:
Method invocation
(§15.12). The particular method used for an
invocation o.m(...)
is chosen based on the
methods that are part of the class or interface that is the type
of o
. For instance methods, the class of the
object referenced by the run-time value of o
participates because a subclass may override a specific method
already declared in a parent class so that this overriding method
is invoked. (The overriding method may or may not choose to
further invoke the original overridden m
method.)
The instanceof
operator (§15.20.2). An expression whose type
is a reference type may be tested using instanceof
to find out
whether the class of the object referenced by the run-time value of
the expression is assignment compatible
(§5.2) with some other reference type.
Casting (§5.5, §15.16). The class of the object referenced by the run-time value of the operand expression might not be compatible with the type specified by the cast. For reference types, this may require a run-time check that throws an exception if the class of the referenced object, as determined at run time, is not assignment compatible (§5.2) with the target type.
Assignment to an array
component of reference type (§10.5,
§15.13, §15.26.1). The
type-checking rules allow the array type S[]
to be
treated as a subtype of T[]
if S is a subtype of T,
but this requires a run-time check for assignment to an array
component, similar to the check performed for a cast.
Exception handling
(§14.20). An exception is caught by a catch
clause only if the class of the thrown exception object is an
instanceof
the type of the formal parameter of the catch
clause.
Situations where the class of an object is not statically known may lead to run-time type errors.
In addition, there are situations where the statically known type may not be accurate at run time. Such situations can arise in a program that gives rise to compile-time unchecked warnings. Such warnings are given in response to operations that cannot be statically guaranteed to be safe, and cannot immediately be subjected to dynamic checking because they involve non-reifiable (§4.7) types. As a result, dynamic checks later in the course of program execution may detect inconsistencies and result in run-time type errors.
A run-time type error can occur only in these situations:
In a cast, when the
actual class of the object referenced by the value of the operand
expression is not compatible with the target type specified by the
cast operator (§5.5,
§15.16); in this case a ClassCastException
is
thrown.
In an automatically generated cast introduced to ensure the validity of an operation on a non-reifiable type (§4.7).
In an assignment to an
array component of reference type, when the actual class of the
object referenced by the value to be assigned is not compatible
with the actual run-time component type of the array
(§10.5, §15.13,
§15.26.1); in this case an ArrayStoreException
is
thrown.
When an exception is not caught
by any catch
clause of a try
statement
(§14.20); in this case the thread of control
that encountered the exception first attempts to invoke an uncaught exception handler
(§11.3) and then
terminates.
Every expression has a normal mode of evaluation in which certain computational steps are carried out. The following sections describe the normal mode of evaluation for each kind of expression.
If all the steps are carried out without an exception being thrown, the expression is said to complete normally.
If, however, evaluation of an
expression throws an exception, then the expression is said
to complete abruptly. An abrupt completion always
has an associated reason, which is always a throw
with a given
value.
Run-time exceptions are thrown by the predefined operators as follows:
A class instance
creation expression (§15.9), array creation
expression (§15.10), array
initializer expression (§10.6),
or string concatenation operator expression
(§15.18.1) throws an OutOfMemoryError
if there is
insufficient memory available.
An array creation
expression (§15.10) throws a
NegativeArraySizeException
if the value of any dimension
expression is less than zero.
A field access
expression (§15.11) throws a NullPointerException
if the
value of the object reference expression is null
.
A method invocation
expression (§15.12) that invokes an instance
method throws a NullPointerException
if the target reference is null
.
An array access
expression (§15.13) throws a NullPointerException
if the
value of the array reference expression is null
.
An array access
expression (§15.13) throws an ArrayIndexOutOfBoundsException
if the
value of the array index expression is negative or greater than or
equal to the length
of the array.
A cast expression
(§15.16) throws a ClassCastException
if a cast is found to
be impermissible at run time.
An integer division
(§15.17.2) or integer remainder
(§15.17.3) operator throws an
ArithmeticException
if the value of the right-hand operand
expression is zero.
An assignment to an
array component of reference type (§15.26.1),
a method invocation expression (§15.12), or a
prefix or postfix increment (§15.14.2,
§15.15.1) or decrement operator
(§15.14.3, §15.15.2) may
all throw an OutOfMemoryError
as a result of boxing conversion
(§5.1.7).
An assignment to an
array component of reference type (§15.26.1)
throws an ArrayStoreException
when the value to be assigned is not compatible
with the component type of the array
(§10.5).
A method invocation expression can also result in an exception being thrown if an exception occurs that causes execution of the method body to complete abruptly.
A class instance creation expression can also result in an exception being thrown if an exception occurs that causes execution of the constructor to complete abruptly.
Various linkage and virtual machine errors may also occur during the evaluation of an expression. By their nature, such errors are difficult to predict and difficult to handle.
If an exception occurs, then evaluation of one or more expressions may be terminated before all steps of their normal mode of evaluation are complete; such expressions are said to complete abruptly.
If evaluation of an expression requires evaluation of a subexpression, then abrupt completion of the subexpression always causes the immediate abrupt completion of the expression itself, with the same reason, and all succeeding steps in the normal mode of evaluation are not performed.
The terms "complete normally" and "complete abruptly" are also applied to the execution of statements (§14.1). A statement may complete abruptly for a variety of reasons, not just because an exception is thrown.
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
It is recommended that code not rely crucially on this specification. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.
The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.
If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.
If evaluation of the left-hand operand of a binary operator completes abruptly, no part of the right-hand operand appears to have been evaluated.
Example 15.7.1-1. Left-Hand Operand Is Evaluated First
In the following program, the *
operator
has a left-hand operand that contains an assignment to a variable and
a right-hand operand that contains a reference to the same
variable. The value produced by the reference will reflect the fact
that the assignment occurred first.
class Test1 { public static void main(String[] args) { int i = 2; int j = (i=3) * i; System.out.println(j); } }
This program produces the output:
9
It is not permitted for evaluation of the *
operator to produce 6
instead
of 9
.
Example 15.7.1-2. Implicit Left-Hand Operand In Operator Of Compound Assigment
In the following program, the two assignment
statements both fetch and remember the value of the left-hand operand,
which is 9
, before the right-hand operand of the
addition operator is evaluated, at which point the variable is set
to 3
.
class Test2 { public static void main(String[] args) { int a = 9; a += (a = 3); // first example System.out.println(a); int b = 9; b = b + (b = 3); // second example System.out.println(b); } }
This program produces the output:
12 12
It is not permitted for either assignment (compound
for a
, simple for b
) to produce
the result 6
.
See also the example in §15.26.2.
Example 15.7.1-3. Abrupt Completion of Evaluation of the Left-Hand Operand
class Test3 { public static void main(String[] args) { int j = 1; try { int i = forgetIt() / (j = 2); } catch (Exception e) { System.out.println(e); System.out.println("Now j = " + j); } } static int forgetIt() throws Exception { throw new Exception("I'm outta here!"); } }
This program produces the output:
java.lang.Exception: I'm outta here! Now j = 1
That is, the left-hand
operand forgetIt()
of the operator /
throws
an exception before the right-hand operand is evaluated and its
embedded assignment of 2
to j
occurs.
The
Java programming language guarantees that every operand of an operator (except the
conditional operators &&
, ||
, and ? :
) appears to be
fully evaluated before any part of the operation itself is
performed.
If the
binary operator is an integer division /
(§15.17.2) or integer remainder %
(§15.17.3), then its execution may raise an
ArithmeticException
, but this exception is thrown only after both
operands of the binary operator have been evaluated and only if these
evaluations completed normally.
Example 15.7.2-1. Evaluation of Operands Before Operation
class Test { public static void main(String[] args) { int divisor = 0; try { int i = 1 / (divisor * loseBig()); } catch (Exception e) { System.out.println(e); } } static int loseBig() throws Exception { throw new Exception("Shuffle off to Buffalo!"); } }
This program produces the output:
java.lang.Exception: Shuffle off to Buffalo!
and not:
java.lang.ArithmeticException: / by zero
since no part of the division operation, including
signaling of a divide-by-zero exception, may appear to occur before
the invocation of loseBig
completes, even though
the implementation may be able to detect or infer that the division
operation would certainly result in a divide-by-zero exception.
The Java programming language respects the order of evaluation indicated explicitly by parentheses and implicitly by operator precedence.
An implementation of the Java programming language may not take advantage of algebraic identities such as the associative law to rewrite expressions into a more convenient computational order unless it can be proven that the replacement expression is equivalent in value and in its observable side effects, even in the presence of multiple threads of execution (using the thread execution model in §17), for all possible computational values that might be involved.
In the case of floating-point calculations, this rule applies also for infinity and not-a-number (NaN) values.
For example, !(x<y)
may not be
rewritten as x>=y
, because these expressions
have different values if either x
or y
is NaN or both are NaN.
Specifically, floating-point calculations that appear to be mathematically associative are unlikely to be computationally associative. Such computations must not be naively reordered.
For example, it is not correct for a Java compiler
to rewrite 4.0*x*0.5
as 2.0*x
;
while roundoff happens not to be an issue here, there are large values
of x
for which the first expression produces
infinity (because of overflow) but the second expression produces a
finite result.
So, for example, the test program:
strictfp class Test { public static void main(String[] args) { double d = 8e+307; System.out.println(4.0 * d * 0.5); System.out.println(2.0 * d); } }
prints:
Infinity 1.6e+308
because the first expression overflows and the second does not.
In contrast, integer addition and multiplication are provably associative in the Java programming language.
For example a+b+c
,
where a
, b
,
and c
are local variables (this simplifying
assumption avoids issues involving multiple threads
and volatile
variables), will always produce the
same answer whether evaluated as (a+b)+c
or a+(b+c)
; if the
expression b+c
occurs nearby in the code, a smart
Java compiler may be able to use this common subexpression.
In a method or constructor invocation or class instance creation expression, argument expressions may appear within the parentheses, separated by commas. Each argument expression appears to be fully evaluated before any part of any argument expression to its right.
If evaluation of an argument expression completes abruptly, no part of any argument expression to its right appears to have been evaluated.
Example 15.7.4-1. Evaluation Order At Method Invocation
class Test1 { public static void main(String[] args) { String s = "going, "; print3(s, s, s = "gone"); } static void print3(String a, String b, String c) { System.out.println(a + b + c); } }
This program produces the output:
going, going, gone
because the assignment of the string
"gone
" to s
occurs after the
first two arguments to print3
have been
evaluated.
Example 15.7.4-2. Abrupt Completion of Argument Expression
class Test2 { static int id; public static void main(String[] args) { try { test(id = 1, oops(), id = 3); } catch (Exception e) { System.out.println(e + ", id=" + id); } } static int test(int a, int b, int c) { return a + b + c; } static int oops() throws Exception { throw new Exception("oops"); } }
This program produces the output:
java.lang.Exception: oops, id=1
because the assignment of 3
to id
is not executed.
Primary expressions include most of the simplest kinds of expressions, from which all others are constructed: literals, class literals, field accesses, method invocations, and array accesses. A parenthesized expression is also treated syntactically as a primary expression.
Primary:
PrimaryNoNewArray
ArrayCreationExpression
PrimaryNoNewArray:
Literal
Type .
class
void
.
class
this
ClassName .
this
(
Expression )
ClassInstanceCreationExpression
FieldAccess
MethodInvocation
ArrayAccess
This part of the grammar of the Java programming language is unusual, in two ways. First, one might expect simple names, such as names of local variables and method parameters, to be primary expressions. For technical reasons, names are grouped together with primary expressions a little later when postfix expressions are introduced (§15.14).
The technical
reasons have to do with allowing left-to-right parsing of Java
programs with only one-token lookahead. Consider the
expressions (z[3])
and (z[])
. The first is a parenthesized array
access (§15.13) and the second is the start of a
cast (§15.16). At the point that the look-ahead
symbol is [
, a left-to-right parse will have reduced
the z
to the
nonterminal Name. In the context of a cast we
prefer not to have to reduce the name to
a Primary, but if Name were
one of the alternatives for Primary, then we
could not tell whether to do the reduction (that is, we could not
determine whether the current situation would turn out to be a
parenthesized array access or a cast) without looking ahead two
tokens, to the token following the [
. The Java grammar
presented here avoids the problem by keeping Name
and Primary separate and allowing either in
certain other syntax rules (those for
MethodInvocation, ArrayAccess,
PostfixExpression, but not
for FieldAccess, because this uses an identifier directly). This strategy effectively defers
the question of whether a Name should be treated
as a Primary until more context can be
examined.
The second unusual
feature avoids a potential grammatical ambiguity in the expression
"new int[3][3]
" which in Java always means a single
creation of a multidimensional array, but which, without appropriate
grammatical finesse, might also be interpreted as meaning the same as
"(new int[3])[3]
".
This ambiguity is eliminated by splitting the expected definition of Primary into Primary and PrimaryNoNewArray. (This may be compared to the splitting of Statement into Statement and StatementNoShortIf (§14.5) to avoid the "dangling else" problem.)
A literal (§3.10) denotes a fixed, unchanging value.
The following production from §3.10 is repeated here for convenience:
Literal:
IntegerLiteral
FloatingPointLiteral
BooleanLiteral
CharacterLiteral
StringLiteral
NullLiteral
The type of a literal is determined as follows:
The type of an integer
literal (§3.10.1) that ends
with L
or l
is long
(§4.2.1).
The type of any
other integer literal is int
(§4.2.1).
The type of a
floating-point literal (§3.10.2) that ends
with F
or f
is float
and
its value must be an element of the float value set
(§4.2.3).
The type of any
other floating-point literal is double
and its value must be an
element of the double value set
(§4.2.3).
The type of a boolean
literal (§3.10.3) is boolean
(§4.2.5).
The type of the null
literal null
(§3.10.7) is the null type
(§4.1); its value is the null
reference.
A class
literal is an expression consisting of the name of a class,
interface, array, or primitive type, or the pseudo-type void
,
followed by a '.
' and the token class
.
The type
of C.
class
, where C is the name of a class, interface, or
array type (§4.3), is
Class
<
C>
.
The type
of p.
class
, where p is the name of a
primitive type (§4.2), is
Class
<
B>
, where B is the
type of an expression of type p after boxing conversion
(§5.1.7).
The type of
void
.
class
(§8.4.5) is
Class
<
Void
>
.
It is a compile-time error if the named type is a type variable (§4.4) or a parameterized type (§4.5) or an array whose element type is a type variable or parameterized type.
It is a compile-time error if the named type does not denote a type that is accessible (§6.6) and in scope (§6.3) at the point where the class literal appears.
A class
literal evaluates to the Class
object for the named type (or for
void
) as defined by the defining class loader
(§12.2) of the class of the current
instance.
The
keyword this
may be used only in the body of an instance method,
instance initializer, or constructor, or in the initializer of an
instance variable of a class. If it appears anywhere else, a
compile-time error occurs.
When
used as a primary expression, the keyword this
denotes a value that
is a reference to the object for which the instance method was invoked
(§15.12), or to the object being
constructed.
The type
of this
is the class C within which the keyword this
occurs.
At run time, the class of the actual object referred to may be the class C or any subclass of C.
The keyword this
is also
used in a special explicit constructor invocation statement, which can
appear at the beginning of a constructor body
(§8.8.7).
Example 15.8.3-1. The this
Expression
class IntVector { int[] v; boolean equals(IntVector other) { if (this == other) return true; if (v.length != other.v.length) return false; for (int i = 0; i < v.length; i++) { if (v[i] != other.v[i]) return false; } return true; } }
Here, the class IntVector
implements a method equals
, which compares two
vectors. If the other vector is the same vector object as the one for
which the equals
method was invoked, then the check
can skip the length and value
comparisons. The equals
method implements this
check by comparing the reference to the other object to this
.
Any
lexically enclosing instance (§8.1.3) can be
referred to by explicitly qualifying the keyword this
.
Let C
be the class denoted by ClassName. Let n be
an integer such that C is the n'th lexically enclosing class of
the class in which the qualified this
expression appears.
The value
of an expression of the form ClassName.
this
is the n'th lexically enclosing instance of this
.
The type of the expression is C.
It is a compile-time error if the current class is not an inner class of class C or C itself.
A parenthesized expression is a primary expression whose type is the type of the contained expression and whose value at run time is the value of the contained expression. If the contained expression denotes a variable then the parenthesized expression also denotes that variable.
The use
of parentheses affects only the order of
evaluation, except for a corner case
whereby (-2147483648)
and (-9223372036854775808L)
are legal
but -(2147483648)
and -(9223372036854775808L)
are illegal.
This is because the decimal literals
2147483648
and 9223372036854775808L
are allowed only as an
operand of the unary minus operator
(§3.10.1).
In particular, the presence or absence of parentheses around an expression does not (except for the case noted above) affect in any way:
A class instance creation expression is used to create new objects that are instances of classes.
ClassInstanceCreationExpression:
new
TypeArgumentsopt TypeDeclSpecifier TypeArgumentsOrDiamondopt
(
ArgumentListopt )
ClassBodyopt
Primary .
new
TypeArgumentsopt Identifier TypeArgumentsOrDiamondopt
(
ArgumentListopt )
ClassBodyopt
TypeArgumentsOrDiamond:
TypeArguments
<>
ArgumentList:
Expression
ArgumentList ,
Expression
A class instance
creation expression specifies a class to be instantiated, possibly
followed by type arguments (§4.5.1) or
a diamond ("<>
") if the class being
instantiated is generic (§8.1.2), followed by (a
possibly empty) list of actual value arguments to the
constructor.
If the type
argument list to the class is empty — the diamond form "<>
" —
the type arguments of the class are inferred. It is legal, though
strongly discouraged as a matter of style, to have white space between
the "<
" and ">
" of a diamond.
If the
constructor is generic (§8.8.4), the type
arguments to the constructor may similarly either be inferred or
passed explicitly. If passed explicitly, the type arguments to the
constructor immediately follow the keyword new
.
It is a
compile-time error if a class instance creation expression provides a
constructor type argument list but uses "<>
" in place of a
class type argument list.
This rule is introduced because inference of a generic class's type arguments may influence the constraints on a generic constructor's type arguments.
It is a compile-time error if any of the type arguments used in a class instance creation expression are wildcard type arguments (§4.5.1).
The exception types that a class instance creation expression can throw are specified in §11.2.1.
Class instance creation expressions have two forms:
Unqualified
class instance creation expressions begin with the
keyword new
.
An unqualified class instance creation expression may be used to create an instance of a class, regardless of whether the class is a top level (§7.6), member (§8.5, §9.5), local (§14.3) or anonymous class (§15.9.5).
Qualified class instance creation expressions begin with a Primary.
A qualified class instance creation expression enables the creation of instances of inner member classes and their anonymous subclasses.
Both unqualified and qualified class instance creation expressions may optionally end with a class body. Such a class instance creation expression declares an anonymous class (§15.9.5) and creates an instance of it.
It is a
compile-time error if a class instance creation expression declares an
anonymous class using the "<>
" form for the class's type
arguments.
We say that a class is instantiated when an instance of the class is created by a class instance creation expression. Class instantiation involves determining what class is to be instantiated, what the enclosing instances (if any) of the newly created instance are, what constructor should be invoked to create the new instance, and what arguments should be passed to that constructor.
If the class instance creation expression ends in a class body, then the class being instantiated is an anonymous class. Then:
If the class instance creation expression is an unqualified class instance creation expression, then TypeDeclSpecifier TypeArgumentsOrDiamondopt must denote a type, T (§4.3, §4.5), or a compile-time error occurs.
It is a compile-time error if the class or interface named by T is not accessible (§6.6) or if T is an enum type (§8.9).
If
T denotes a class, then an anonymous direct subclass of the
class named by T is declared. It is a compile-time error if the
class denoted by T is a final
class.
If T denotes an interface, then an
anonymous direct subclass of Object
that implements the
interface named by T is declared.
In either case, the body of the subclass is the ClassBody given in the class instance creation expression.
Otherwise, the class instance creation expression is a qualified class instance creation expression.
It is a
compile-time error if the Identifier after the new
token is not the simple name (§6.2) of an
accessible (§6.6) non-final
inner class
(§8.1.3) that is a member of the compile-time
type of the Primary.
It is a compile-time error if the name is ambiguous (§8.5) or denotes an enum type.
When TypeArguments are provided after the name, it is a compile-time error if the type arguments, when applied to the named class, do not denote a well-formed parameterized type (§4.5).
Let T be the type named by the Identifier and any type arguments. An anonymous direct subclass of the class named by T is declared. The body of the subclass is the ClassBody given in the class instance creation expression.
If a class instance creation expression does not declare an anonymous class, then:
If the class
instance creation expression is an unqualified class instance
creation expression, then
the TypeDeclSpecifier must denote a class
that is accessible (§6.6) and is not an enum
type and not abstract
, or a compile-time error occurs.
When TypeArguments are provided after the class name, it is a compile-time error if the type arguments, when applied to the class, do not denote a well-formed parameterized type (§4.5).
When
"<>
" is used to elide type arguments after the class name,
it is a compile-time error if the class is not generic.
The class being instantiated is the class denoted by the TypeDeclSpecifier.
Otherwise, the class instance creation expression is a qualified class instance creation expression.
It
is a compile-time error if the Identifier after the new
token
is not the simple name (§6.2) of an
accessible (§6.6) non-abstract
inner class
(§8.1.3) C that is a member of the
compile-time type of the Primary.
It is a compile-time error if the name is ambiguous (§8.5) or denotes an enum type (§8.9).
When TypeArguments are provided after the name, it is a compile-time error if the type arguments, when applied to C, do not denote a well-formed parameterized type (§4.5).
When
"<>
" is used to elide type arguments after the name, it is
a compile-time error if C is not generic.
The type of the class instance creation expression is the class type being instantiated, as defined above.
Let C
be the class being instantiated, and let i
be the
instance being created. If C is an inner class
then i
may have an immediately enclosing
instance. The immediately enclosing instance of i
(§8.1.3) is determined as follows.
If C is an anonymous class, then:
If
the class instance creation expression occurs in a static context
(§8.1.3), then i
has no
immediately enclosing instance.
If C is a local class (§14.3), then let O be the innermost lexically enclosing class of C. Let n be an integer such that O is the n'th lexically enclosing class of the class in which the class instance creation expression appears. Then:
If
C occurs in a static context, then i
has no
immediately enclosing instance.
Otherwise, if the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, the immediately enclosing
instance of i
is the n'th lexically
enclosing instance of this
(§8.1.3).
Otherwise, C is an inner member class (§8.5), and then:
If the class instance creation expression is an unqualified class instance creation expression, then:
If the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, if C is a member of an enclosing class then let O be the innermost lexically enclosing class of which C is a member, and let n be an integer such that O is the n'th lexically enclosing class of the class in which the class instance creation expression appears.
The immediately enclosing
instance of i
is
the n'th lexically enclosing instance of
this
.
Otherwise, the class instance creation expression is a qualified class instance creation expression.
The immediately enclosing instance
of i
is the object that is the value of the
Primary expression.
In
addition, if C is an anonymous class, and the direct superclass of
C, S, is an inner class, then i
may have an
immediately enclosing instance with respect to S. It is determined
as follows.
If S is a local class (§14.3), then let O be the innermost lexically enclosing class of S. Let n be an integer such that O is the n'th lexically enclosing class of the class in which the class instance creation expression appears. Then:
If
S occurs within a static context, then i
has
no immediately enclosing instance with respect to S.
Otherwise, if the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, the immediately enclosing
instance of i
with respect to S is the
n'th lexically enclosing instance of this
.
Otherwise, S is an inner member class (§8.5), and then:
If the class instance creation expression is an unqualified class instance creation expression, then:
If the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, if S is a member of an enclosing class, then let O be the innermost lexically enclosing class of which S is a member, and let n be an integer such that O is the n'th lexically enclosing class of the class in which the class instance creation expression appears.
The immediately enclosing
instance of i
with respect to S is the
n'th lexically enclosing instance of this
.
Otherwise, the class instance creation expression is a qualified class instance creation expression.
The immediately enclosing instance
of i
with respect to S is the object that is
the value of the Primary expression.
Let C
be the class being instantiated. To create an instance of
C, i
, a constructor of C is chosen at
compile time by the following rules.
First, the actual arguments to the constructor invocation are determined:
If C is an anonymous class, and the direct superclass of C, S, is an inner class, then:
If S is a local class and S occurs in a static context, then the arguments in the argument list, if any, are the arguments to the constructor, in the order they appear in the expression.
Otherwise, the immediately
enclosing instance of i
with respect to S
is the first argument to the constructor, followed by the
arguments in the argument list of the class instance creation
expression, if any, in the order they appear in the
expression.
Otherwise the arguments in the argument list, if any, are the arguments to the constructor, in the order they appear in the expression.
Second, a
constructor of C and corresponding result type and throws
clause
are determined:
If
the class instance creation expression uses "<>
" to elide
class type arguments, a list of methods m1
...mn
is defined for
the purpose of overload resolution and type argument
inference.
Let c1
...cn
be
the constructors of class C. Let #m
be an
automatically generated name that is distinct from all constructor
and method names in C. For all j (1 ≤ j ≤
n), mj
is defined in terms of cj
as follows:
A substitution
θj
is first defined to instantiate the types in
cj
.
Let
F1...Fp be the type parameters of C, and let G1...Gq
be the type parameters (if any) of cj
. Let X1...Xp and
Y1...Yq be type variables with distinct names that are not
in scope in the body of C.
The type
parameters of mj
are X1...Xp,Y1...Yq. The bound of
each parameter, if any, is θj
applied to the
corresponding parameter bound in C or cj
.
The (possibly
empty) list of argument types of mj
is θj
applied to
the argument types of cj
.
The (possibly
empty) list of thrown types of mj
is θj
applied to the
thrown types of cj
.
To choose a constructor, we
temporarily consider m1
...mn
to be members of C. Then one of
m1
...mn
is selected, as determined by the class instance
creation's argument expressions, using the process described in
§15.12.2.
It is a compile-time error if there is no unique most-specific method that is both applicable and accessible.
Otherwise, where mj
is the selected
method, cj
is the chosen constructor. The result type and
throws
clause of cj
are the same as the return type and
throws
clause determined for mj
(§15.12.2.6).
Otherwise, the class instance creation
expression does not use "<>
" to elide class type
arguments.
Let T be the type denoted by C
followed by any class type arguments in the expression. The
process described in §15.12.2, modified to
handle constructors, is used to select one of the constructors of
T and determine its throws
clause.
As in method invocations, it is a compile-time error if there is no unique most-specific constructor that is both applicable and accessible.
Finally, the type of the class instance creation expression is the result type of the chosen constructor, as defined above.
Note that the type of the class instance creation expression may be an anonymous class type, in which case the constructor being invoked is an anonymous constructor (§15.9.5.1).
At run time, evaluation of a class instance creation expression is as follows.
First,
if the class instance creation expression is a qualified class
instance creation expression, the qualifying primary expression is
evaluated. If the qualifying expression evaluates to null
, a NullPointerException
is raised, and the class instance creation expression completes
abruptly. If the qualifying expression completes abruptly, the class
instance creation expression completes abruptly for the same
reason.
Next,
space is allocated for the new class instance. If there is
insufficient space to allocate the object, evaluation of the class
instance creation expression completes abruptly by throwing an
OutOfMemoryError
.
The new object contains new instances of all the fields declared in the specified class type and all its superclasses. As each new field instance is created, it is initialized to its default value (§4.12.5).
Next, the actual arguments to the constructor are evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.
Next, the selected constructor of the specified class type is invoked. This results in invoking at least one constructor for each superclass of the class type. This process can be directed by explicit constructor invocation statements (§8.8) and is described in detail in §12.5.
The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created.
Example 15.9.4-1. Evaluation Order and Out-Of-Memory Detection
If evaluation of a class instance creation
expression finds there is insufficient memory to perform the creation
operation, then an OutOfMemoryError
is thrown. This check occurs before any
argument expressions are evaluated.
So, for example, the test program:
class List { int value; List next; static List head = new List(0); List(int n) { value = n; next = head; head = this; } } class Test { public static void main(String[] args) { int id = 0, oldid = 0; try { for (;;) { ++id; new List(oldid = id); } } catch (Error e) { List.head = null; System.out.println(e.getClass() + ", " + (oldid==id)); } } }
prints:
class java.lang.OutOfMemoryError, false
because the out-of-memory condition is detected
before the argument expression oldid = id
is
evaluated.
Compare this to the treatment of array creation expressions (§15.10), for which the out-of-memory condition is detected after evaluation of the dimension expressions (§15.10.1).
An anonymous class declaration is automatically derived from a class instance creation expression by the Java compiler.
An
anonymous class is never abstract
(§8.1.1.1).
An
anonymous class is always implicitly final
(§8.1.1.2).
An
anonymous class is always an inner class
(§8.1.3); it is never static
(§8.1.1, §8.5.1).
An anonymous class cannot have an explicitly declared constructor. Instead, a Java compiler must automatically provide an anonymous constructor for the anonymous class. The form of the anonymous constructor of an anonymous class C with direct superclass S is as follows:
If S is not an inner class, or if S is a local class that occurs in a static context, then the anonymous constructor has one formal parameter for each actual argument to the class instance creation expression in which C is declared.
The actual arguments to the class
instance creation expression are used to determine a
constructor cs
of S, using the same rules
as for method invocations (§15.12).
The type of each formal parameter
of the anonymous constructor must be identical to the
corresponding formal parameter of cs
.
The body of the constructor
consists of an explicit constructor invocation
(§8.8.7.1) of the
form super(...)
, where the actual arguments
are the formal parameters of the constructor, in the order they
were declared.
Otherwise, the first formal parameter
of the constructor of C represents the value of the immediately
enclosing instance of i
with respect to
S. The type of this parameter is the class type that immediately
encloses the declaration of S.
The constructor has an additional
formal parameter for each actual argument to the class instance
creation expression that declared the anonymous
class. The n'th formal
parameter e
corresponds to
the n-1'th actual argument.
The actual arguments to the class
instance creation expression are used to determine a
constructor cs
of S, using the same rules as
for method invocations (§15.12).
The type of each formal parameter of
the anonymous constructor must be identical to the corresponding
formal parameter of cs
.
The body of the constructor consists
of an explicit constructor invocation
(§8.8.7.1) of the
form o.super(...)
, where o
is the first formal parameter of the constructor, and the actual
arguments are the subsequent formal parameters of the constructor,
in the order they were declared.
In all
cases, the throws
clause of an anonymous constructor must list all
the checked exceptions thrown by the explicit superclass constructor
invocation statement contained within the anonymous constructor, and
all checked exceptions thrown by any instance initializers or instance
variable initializers of the anonymous class.
Note
that it is possible for the signature of the anonymous constructor to
refer to an inaccessible type (for example, if such a type occurred in
the signature of the superclass constructor cs
).
This does not, in itself, cause any errors at either compile-time or
run-time.
An array creation expression is used to create new arrays (§10).
ArrayCreationExpression:
new
PrimitiveType DimExprs Dimsopt
new
ClassOrInterfaceType DimExprs Dimsopt
new
PrimitiveType Dims ArrayInitializer
new
ClassOrInterfaceType Dims ArrayInitializer
DimExprs:
DimExpr
DimExprs DimExpr
DimExpr:
[
Expression ]
Dims:
[
]
Dims [
]
An array creation expression creates an object that is a new array whose elements are of the type specified by the PrimitiveType or ClassOrInterfaceType.
It is a
compile-time error if the ClassOrInterfaceType
does not denote a reifiable type
(§4.7). Otherwise,
the ClassOrInterfaceType may name any named
reference type, even an abstract
class type
(§8.1.1.1) or an interface type
(§9).
The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard.
The type of each dimension expression within a DimExpr must be a type that is convertible (§5.1.8) to an integral type, or a compile-time error occurs.
Each
dimension expression undergoes unary numeric promotion
(§5.6.1). The promoted type must be int
, or a
compile-time error occurs.
The type
of the array creation expression is an array type that can denoted by
a copy of the array creation expression from which the new
keyword
and every DimExpr expression and array
initializer have been deleted.
For example, the type of the creation expression:
new double[3][3][]
is:
double[][][]
At run time, evaluation of an array creation expression behaves as follows:
If there are no dimension expressions, then there must be an array initializer.
A newly allocated array will be initialized with the values provided by the array initializer as described in §10.6.
The value of the array initializer becomes the value of the array creation expression.
Otherwise, there is no array initializer, and:
First, the dimension expressions are evaluated, left-to-right. If any of the expression evaluations completes abruptly, the expressions to the right of it are not evaluated.
Next, the values of the
dimension expressions are checked. If the value of
any DimExpr expression is less than
zero, then a NegativeArraySizeException
is thrown.
Next, space is allocated for
the new array. If there is insufficient space to allocate
the array, evaluation of the array creation expression
completes abruptly by throwing an OutOfMemoryError
.
Then, if a single DimExpr appears, a one-dimensional array is created of the specified length, and each component of the array is initialized to its default value (§4.12.5).
Otherwise, if
n DimExpr expressions appear, then
array creation effectively executes a set of nested loops of
depth n-1
to create the implied
arrays of arrays.
A multidimensional array need not have arrays of the same length at each level.
Example 15.10.1-1. Array Creation Evaluation
In an array creation expression with one or more dimension expressions, each dimension expression is fully evaluated before any part of any dimension expression to its right. Thus:
class Test1 { public static void main(String[] args) { int i = 4; int ia[][] = new int[i][i=3]; System.out.println( "[" + ia.length + "," + ia[0].length + "]"); } }
prints:
[4,3]
because the first dimension is calculated
as 4
before the second dimension expression
sets i
to 3
.
If evaluation of a dimension expression completes abruptly, no part of any dimension expression to its right will appear to have been evaluated. Thus:
class Test2 { public static void main(String[] args) { int[][] a = { { 00, 01 }, { 10, 11 } }; int i = 99; try { a[val()][i = 1]++; } catch (Exception e) { System.out.println(e + ", i=" + i); } } static int val() throws Exception { throw new Exception("unimplemented"); } }
prints:
java.lang.Exception: unimplemented, i=99
because the embedded assignment that
sets i
to 1
is never
executed.
Example 15.10.1-2. Multi-Dimensional Array Creation
The declaration:
float[][] matrix = new float[3][3];
is equivalent in behavior to:
float[][] matrix = new float[3][]; for (intd
= 0;d
< matrix.length;d
++) matrix[d
] = new float[3];
and:
Age[][][][][] Aquarius = new Age[6][10][8][12][];
is equivalent to:
Age[][][][][] Aquarius = new Age[6][][][][]; for (intd1
= 0;d1
< Aquarius.length;d1
++) { Aquarius[d1
] = new Age[10][][][]; for (intd2
= 0;d2
< Aquarius[d1
].length;d2
++) { Aquarius[d1
][d2
] = new Age[8][][]; for (intd3
= 0;d3
< Aquarius[d1
][d2
].length;d3
++) { Aquarius[d1
][d2
][d3
] = new Age[12][]; } } }
with d
, d1
,
d2
, and d3
replaced by names
that are not already locally declared. Thus, a single new
expression
actually creates one array of length 6, 6 arrays of length 10, 6x10 =
60 arrays of length 8, and 6x10x8 = 480 arrays of length 12. This
example leaves the fifth dimension, which would be arrays containing
the actual array elements (references to Age
objects), initialized only to null references. These arrays can be
filled in later by other code, such as:
Age[] Hair = { new Age("quartz"), new Age("topaz") }; Aquarius[1][9][6][9] = Hair;
A triangular matrix may be created by:
float triang[][] = new float[100][]; for (int i = 0; i < triang.length; i++) triang[i] = new float[i+1];
If
evaluation of an array creation expression finds there is insufficient
memory to perform the creation operation, then an OutOfMemoryError
is
thrown. If the array
creation expression does not have an array initializer, then this
check occurs only after evaluation of all dimension expressions has
completed normally. If the array creation expression does have an
array initializer, then an OutOfMemoryError
can occur when an object of
reference type is allocated during evaluation of a variable
initializer expression, or when space is allocated for an array to
hold the values of a (possibly nested) array initializer.
Example 15.10.1-3. OutOfMemoryError
and Dimension Expression Evaluation
class Test3 { public static void main(String[] args) { int len = 0, oldlen = 0; Object[] a = new Object[0]; try { for (;;) { ++len; Object[] temp = new Object[oldlen = len]; temp[0] = a; a = temp; } } catch (Error e) { System.out.println(e + ", " + (oldlen==len)); } } }
This program produces the output:
java.lang.OutOfMemoryError, true
because the out-of-memory condition is detected
after the dimension expression oldlen = len
is
evaluated.
Compare this to class instance creation expressions (§15.9), which detect the out-of-memory condition before evaluating argument expressions (§15.9.4).
A field
access expression may access a field of an object or array, a
reference to which is the value of either an expression or the special
keyword super
.
FieldAccess:
Primary .
Identifier
super
.
Identifier
ClassName .
super
.
Identifier
The meaning of a field access expression is determined using the same rules as for qualified names (§6.5.6.2), but limited by the fact that an expression cannot denote a package, class type, or interface type.
It is also possible to refer to a field of the current instance or current class by using a simple name (§6.5.6.1).
The type of the Primary must be a reference type T, or a compile-time error occurs.
The meaning of the field access expression is determined as follows:
If the identifier names several accessible (§6.6) member fields in type T, then the field access is ambiguous and a compile-time error occurs.
If the identifier does not name an accessible member field in type T, then the field access is undefined and a compile-time error occurs.
Otherwise, the identifier names a single accessible member field in type T, and the type of the field access expression is the type of the member field after capture conversion (§5.1.10).
At run time,
the result of the field access expression is computed as
follows: (assuming that the program is correct with respect
to definite assignment analysis, i.e. every blank final
variable is
definitely assigned before access)
The Primary expression is evaluated, and the result is discarded. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.
If the field
is a non-blank final
field, then the
result is the value of the specified class variable in the
class or interface that is the type of the Primary
expression.
If the field is not
final
, or is a blank final
and the field
access occurs in a constructor, then the result is
a variable, namely, the specified class variable in the
class that is the type of the Primary expression.
The Primary expression is evaluated. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.
If the value of the Primary
is null
, then a NullPointerException
is thrown.
If the field
is a non-blank final
, then the result
is the value of the named member field in type T
found in the object referenced by the value of the
Primary.
If the field is not
final
, or is a blank final
and the field
access occurs in a constructor, then the result is
a variable, namely the named member field in type T
found in the object referenced by the value of the
Primary.
Note that only the type of the Primary expression, not the class of the actual object referred to at run time, is used in determining which field to use.
Example 15.11.1-1. Static Binding for Field Access
class S { int x = 0; } class T extends S { int x = 1; } class Test1 { public static void main(String[] args) { T t = new T(); System.out.println("t.x=" + t.x + when("t", t)); S s = new S(); System.out.println("s.x=" + s.x + when("s", s)); s = t; System.out.println("s.x=" + s.x + when("s", s)); } static String when(String name, Object t) { return " when " + name + " holds a " + t.getClass() + " at run time."; } }
This program produces the output:
t.x=1 when t holds a class T at run time. s.x=0 when s holds a class S at run time. s.x=0 when s holds a class T at run time.
The last line shows that, indeed, the field that is
accessed does not depend on the run-time class of the referenced
object; even if s
holds a reference to an object of
class T
, the expression s.x
refers to the x
field of
class S
, because the type of the
expression s
is S
. Objects of
class T
contain two fields
named x
, one for class T
and one
for its superclass S
.
This lack of dynamic lookup for field accesses allows programs to be run efficiently with straightforward implementations. The power of late binding and overriding is available, but only when instance methods are used. Consider the same example using instance methods to access the fields:
class S { int x = 0; int z() { return x; } } class T extends S { int x = 1; int z() { return x; } } class Test2 { public static void main(String[] args) { T t = new T(); System.out.println("t.z()=" + t.z() + when("t", t)); S s = new S(); System.out.println("s.z()=" + s.z() + when("s", s)); s = t; System.out.println("s.z()=" + s.z() + when("s", s)); } static String when(String name, Object t) { return " when " + name + " holds a " + t.getClass() + " at run time."; } }
Now the output is:
t.z()=1 when t holds a class T at run time. s.z()=0 when s holds a class S at run time. s.z()=1 when s holds a class T at run time.
The last line shows that, indeed, the method that is
accessed does depend on the run-time class of the
referenced object; when s
holds a reference to an
object of class T
, the
expression s.z()
refers to the z
method of class T
, despite the fact that the type
of the expression s
is S
. Method z
of
class T
overrides method z
of
class S
.
Example 15.11.1-2. Receiver Variable Is Irrelevant For static
Field Access
The following program demonstrates that a null
reference may be used to access a class (static
) variable without
causing an exception:
class Test3 { static String mountain = "Chocorua"; static Test3 favorite(){ System.out.print("Mount "); return null; } public static void main(String[] args) { System.out.println(favorite().mountain); } }
It compiles, executes, and prints:
Mount Chocorua
Even though the result
of favorite()
is null
, a NullPointerException
is not
thrown. That "Mount
" is printed demonstrates that
the Primary expression is indeed fully evaluated at run time,
despite the fact that only its type, not its value, is used to
determine which field to access (because the
field mountain
is static
).
The form super
.
Identifier
refers to the field named Identifier of the current object, but with
the current object viewed as an instance of the superclass of the
current class.
The form
T.
super
.
Identifier refers to the field named
Identifier of the lexically enclosing instance corresponding to T,
but with that instance viewed as an instance of the superclass of
T.
The forms
using the keyword super
are valid only in an instance method,
instance initializer, or constructor, or in the initializer of an
instance variable of a class. If they appear anywhere else, a
compile-time error occurs.
These are exactly the same situations in which the
keyword this
may be used (§15.8.3).
It is a
compile-time error if the forms using the keyword super
appear in
the declaration of class Object
, since Object
has no
superclass.
If a field access expression
super
.
name
appears within class C, and the
immediate superclass of C is class S, then
super
.
name
is treated exactly as if it had
been the expression this
.
name
in
the body of class S. Thus it can access the
field name
that is visible in class S, even if
that field is hidden by a declaration of a
field name
in class C.
If a field access expression
T.
super
.
name
appears within class C,
and the immediate superclass of the class denoted by T is a class
whose fully qualified name is S, then
T.
super
.
name
is treated exactly as if
it had been the expression
this
.
name
in the body of class
S. Thus it can access the field name
that is visible in class S, even if that field is hidden by a
declaration of a field name
in class T.
It is a compile-time error if the current class is not an inner class of class T or T itself.
Example 15.11.2-1. The super
Expression
interface I { int x = 0; } class T1 implements I { int x = 1; } class T2 extends T1 { int x = 2; } class T3 extends T2 { int x = 3; void test() { System.out.println("x=\t\t" + x); System.out.println("super.x=\t\t" + super.x); System.out.println("((T2)this).x=\t" + ((T2)this).x); System.out.println("((T1)this).x=\t" + ((T1)this).x); System.out.println("((I)this).x=\t" + ((I)this).x); } } class Test { public static void main(String[] args) { new T3().test(); } }
This program produces the output:
x= 3 super.x= 2 ((T2)this).x= 2 ((T1)this).x= 1 ((I)this).x= 0
Within class T3
, the
expression super.x
is treated as if it were:
((T2)this).x
Note that super.x
is not
specified in terms of a cast, due to difficulties around access to
protected
members of the superclass.
A method invocation expression is used to invoke a class or instance method.
MethodInvocation:
MethodName (
ArgumentListopt )
Primary .
NonWildTypeArgumentsopt Identifier (
ArgumentListopt )
super
.
NonWildTypeArgumentsopt Identifier (
ArgumentListopt )
ClassName .
super
.
NonWildTypeArgumentsopt Identifier (
ArgumentListopt )
TypeName .
NonWildTypeArguments Identifier (
ArgumentListopt )
The definition of ArgumentList from §15.9 is repeated here for convenience:
ArgumentList:
Expression
ArgumentList ,
Expression
Resolving a method name at compile time is more complicated than resolving a field name because of the possibility of method overloading. Invoking a method at run time is also more complicated than accessing a field because of the possibility of instance method overriding.
Determining the method that will be invoked by a method invocation expression involves several steps. The following three sections describe the compile-time processing of a method invocation; the determination of the type of the method invocation expression is described in §15.12.3.
The first step in processing a method invocation at compile time is to figure out the name of the method to be invoked and which class or interface to check for definitions of methods of that name. There are several cases to consider, depending on the form that precedes the left parenthesis, as follows.
If the form is MethodName, then there are three subcases:
If it is a simple name, that is, just an Identifier, then the name of the method is the Identifier.
If the Identifier appears within the scope of a visible method declaration with that name (§6.3, §6.4.1), then:
If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or interface to search is T.
This search policy is called the "comb rule". It effectively looks for methods in a nested class's superclass hierarchy before looking for methods in an enclosing class and its superclass hierarchy. See §6.5.7.1 for an example.
Otherwise, the visible method declaration may be in scope due to one or more single-static-import or static-import-on-demand declarations (§7.5.3, §7.5.4). There is no class or interface to search, as the method to be invoked is determined later (§15.12.2).
If it is a qualified name of
the form TypeName .
Identifier,
then the name of the method is the Identifier and the
class to search is the one named by
the TypeName.
If TypeName
is the name of an interface rather than a class, then a
compile-time error occurs, because this form can invoke only
static
methods and interfaces have no static
methods.
In all other cases, the
qualified name has the form FieldName
.
Identifier.
The name of the method is the Identifier and the class or interface to search is the declared type T of the field named by the FieldName, if T is a class or interface type, or the upper bound of T if T is a type variable.
If
the form is Primary
.
NonWildTypeArgumentsopt
Identifier, then the name of the method is the
Identifier.
Let T be the type of the Primary expression. The class or interface to be searched is T if T is a class or interface type, or the upper bound of T if T is a type variable.
If
the form is super
.
NonWildTypeArgumentsopt Identifier,
then the name of the method is the Identifier and the class to
be searched is the superclass of the class whose declaration
contains the method invocation.
Let T be the type declaration
immediately enclosing the method invocation. It is a
compile-time error if T is the class Object
or T is an
interface.
If
the form is ClassName .
super
.
NonWildTypeArgumentsopt
Identifier, then the name of the method is the Identifier
and the class to be searched is the superclass of the class C
denoted by ClassName.
It is a compile-time error if C is not a lexically enclosing class of the current class.
It is a compile-time error if C
is the class Object
.
Let T be the type declaration
immediately enclosing the method invocation. It is a
compile-time error if T is the class Object
or T is an
interface.
If
the form is TypeName
.
NonWildTypeArguments Identifier,
then the name of the method is the Identifier and the class to
be searched is the class C denoted
by TypeName.
If TypeName is
the name of an interface rather than a class, then a
compile-time error occurs, because this form can invoke only
static
methods and interfaces have no static
methods.
The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is one used at run time to perform the method dispatch.
A method is applicable if it is either applicable by subtyping (§15.12.2.2), applicable by method invocation conversion (§15.12.2.3), or it is an applicable variable arity method (§15.12.2.4).
The process of determining applicability begins by determining the potentially applicable methods (§15.12.2.1).
The remainder of the process is split into three phases, to ensure compatibility with versions of the Java programming language prior to Java SE 5.0. The phases are:
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
This
guarantees that any calls that were valid in the Java programming language
before Java SE 5.0 are not considered ambiguous as the result of
the introduction of variable arity methods, implicit boxing
and/or unboxing. However, the declaration of a variable arity
method (§8.4.1) can change the method
chosen for a given method method invocation expression, because
a variable arity method is treated as a fixed arity method in
the first phase. For example,
declaring m(Object...)
in a class which
already declares m(Object)
causes m(Object)
to no longer be chosen for
some invocation expressions (such
as m(null)
),
as m(Object[])
is more specific.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.
This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method invocation.
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
Deciding whether a method is applicable will, in the case of generic methods (§8.4.4), require that type arguments be determined. Type arguments may be passed explicitly or implicitly. If they are passed implicitly, they must be inferred (§15.12.2.7) from the types of the argument expressions.
If several applicable methods have been identified during one of the three phases of applicability testing, then the most specific one is chosen, as specified in section §15.12.2.5.
Example 15.12.2-1. Method Applicability
class Doubler { static int two() { return two(1); } private static int two(int i) { return 2*i; } } class Test extends Doubler { static long two(long j) { return j+j; } public static void main(String[] args) { System.out.println(two(3)); System.out.println(Doubler.two(3)); // compile-time error } }
For the method invocation two(1)
within class Doubler
, there are two accessible
methods named two
, but only the second one is
applicable, and so that is the one invoked at run time.
For the method invocation two(3)
within class Test
, there are two applicable
methods, but only the one in class Test
is
accessible, and so that is the one to be invoked at run time (the
argument 3
is converted to
type long
).
For the method
invocation Doubler.two(3)
, the
class Doubler
, not class Test
,
is searched for methods named two
; the only
applicable method is not accessible, and so this method invocation
causes a compile-time error.
Another example is:
class ColoredPoint { int x, y; byte color; void setColor(byte color) { this.color = color; } } class Test { public static void main(String[] args) { ColoredPoint cp = new ColoredPoint(); byte color = 37; cp.setColor(color); cp.setColor(37); // compile-time error } }
Here, a compile-time error occurs for the second
invocation of setColor
, because no applicable
method can be found at compile time. The type of the
literal 37
is int
, and int
cannot be converted
to byte
by method invocation conversion. Assignment conversion,
which is used in the initialization of the
variable color
, performs an implicit conversion of
the constant from type int
to byte
, which is permitted because the
value 37
is small enough to be represented in type
byte
; but such a conversion is not allowed for method invocation
conversion.
If the method setColor
had,
however, been declared to take an int
instead of a byte
, then both
method invocations would be correct; the first invocation would be
allowed because method invocation conversion does permit a widening
conversion from byte
to int
. However, a narrowing cast would then
be required in the body of setColor
:
void setColor(int color) { this.color = (byte)color; }
Here is an example of overloading ambiguity. Consider the program:
class Point { int x, y; } class ColoredPoint extends Point { int color; } class Test { static void test(ColoredPoint p, Point q) { System.out.println("(ColoredPoint, Point)"); } static void test(Point p, ColoredPoint q) { System.out.println("(Point, ColoredPoint)"); } public static void main(String[] args) { ColoredPoint cp = new ColoredPoint(); test(cp, cp); // compile-time error } }
This example produces an error at compile time. The
problem is that there are two declarations of test
that are applicable and accessible, and neither is more specific than
the other. Therefore, the method invocation is ambiguous.
If a third definition of test
were added:
static void test(ColoredPoint p, ColoredPoint q) { System.out.println("(ColoredPoint, ColoredPoint)"); }
then it would be more specific than the other two, and the method invocation would no longer be ambiguous.
Example 15.12.2-2. Return Type Not Considered During Method Selection
class Point { int x, y; } class ColoredPoint extends Point { int color; } class Test { static int test(ColoredPoint p) { return p.color; } static String test(Point p) { return "Point"; } public static void main(String[] args) { ColoredPoint cp = new ColoredPoint(); String s = test(cp); // compile-time error } }
Here, the most specific declaration of
method test
is the one taking a parameter of
type ColoredPoint
. Because the result type of the
method is int
, a compile-time error occurs because an int
cannot
be converted to a String
by assignment conversion. This example
shows that the result types of methods do not participate in resolving
overloaded methods, so that the second test
method,
which returns a String
, is not chosen, even though it has a result
type that would allow the example program to compile without
error.
Example 15.12.2-3. Choosing The Most Specific Method
The most specific method is chosen at compile time; its descriptor determines what method is actually executed at run time. If a new method is added to a class, then source code that was compiled with the old definition of the class might not use the new method, even if a recompilation would cause this method to be chosen.
So, for example, consider two compilation units, one
for class Point
:
package points; public class Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public String toString() { return toString(""); } public String toString(String s) { return "(" + x + "," + y + s + ")"; } }
and one for
class ColoredPoint
:
package points; public class ColoredPoint extends Point { public static final int RED = 0, GREEN = 1, BLUE = 2; public static String[] COLORS = { "red", "green", "blue" }; public byte color; public ColoredPoint(int x, int y, int color) { super(x, y); this.color = (byte)color; } /** Copy all relevant fields of the argument into this ColoredPoint object. */ public void adopt(Point p) { x = p.x; y = p.y; } public String toString() { String s = "," + COLORS[color]; return super.toString(s); } }
Now consider a third compilation unit that
uses ColoredPoint
:
import points.*; class Test { public static void main(String[] args) { ColoredPoint cp = new ColoredPoint(6, 6, ColoredPoint.RED); ColoredPoint cp2 = new ColoredPoint(3, 3, ColoredPoint.GREEN); cp.adopt(cp2); System.out.println("cp: " + cp); } }
The output is:
cp: (3,3,red)
The programmer who coded
class Test
has expected to see the
word green
, because the actual argument,
a ColoredPoint
, has a color
field, and color
would seem to be a "relevant
field". (Of course, the documentation for the
package points
ought to have been much more
precise!)
Notice, by the way, that the most specific method
(indeed, the only applicable method) for the method invocation
of adopt
has a signature that indicates a method of
one parameter, and the parameter is of
type Point
. This signature becomes part of the
binary representation of class Test
produced by the
Java compiler and is used by the method invocation at run time.
Suppose the programmer reported this software error
and the maintainer of the points
package decided,
after due deliberation, to correct it by adding a method to
class ColoredPoint
:
public void adopt(ColoredPoint p) { adopt((Point)p); color = p.color; }
If the programmer then runs the old binary file
for Test
with the new binary file
for ColoredPoint
, the output is still:
cp: (3,3,red)
because the old binary file
for Test
still has the descriptor "one parameter,
whose type is Point
; void
" associated with the
method call cp.adopt(cp2)
. If the source code
for Test
is recompiled, the Java compiler will then
discover that there are now two applicable adopt
methods, and that the signature for the more specific one is "one
parameter, whose type is ColoredPoint
; void
";
running the program will then produce the desired output:
cp: (3,3,green)
With forethought about such problems, the maintainer
of the points
package could fix
the ColoredPoint
class to work with both newly
compiled and old code, by adding defensive code to the
old adopt
method for the sake of old code that
still invokes it on ColoredPoint
arguments:
public void adopt(Point p) { if (p instanceof ColoredPoint) color = ((ColoredPoint)p).color; x = p.x; y = p.y; }
Ideally, source code should be recompiled whenever code that it depends on is changed. However, in an environment where different classes are maintained by different organizations, this is not always feasible. Defensive programming with careful attention to the problems of class evolution can make upgraded code much more robust. See §13 for a detailed discussion of binary compatibility and type evolution.
The class or interface determined by compile-time step 1 (§15.12.1) is searched for all member methods that are potentially applicable to this method invocation; members inherited from superclasses and superinterfaces are included in this search.
In addition, if the method invocation has, before the left parenthesis, a MethodName of the form Identifier, then the search process also examines all member methods that are (a) imported by single-static-import declarations (§7.5.3) and static-import-on-demand declarations (§7.5.4) within the compilation unit (§7.3) within which the method invocation occurs, and (b) not shadowed (§6.4.1) at the place where the method invocation appears, to determine if they are potentially applicable.
A member method is potentially applicable to a method invocation if and only if all of the following are true:
The name of the member is identical to the name of the method in the method invocation.
The member is accessible (§6.6) to the class or interface in which the method invocation appears.
Whether a member method is accessible at a
method invocation depends on the access modifier (public
, none,
protected
, or private
) in the member's declaration and on
where the method invocation appears.
If the member is a variable arity
method with arity n, the arity of the method invocation is
greater or equal to n-1
.
If the member is a fixed arity method with arity n, the arity of the method invocation is equal to n.
If the method invocation includes explicit type arguments, and the member is a generic method, then the number of type arguments is equal to the number of type parameters of the method.
This clause implies that a non-generic method may be potentially applicable to an invocation that supplies explicit type arguments. Indeed, it may turn out to be applicable. In such a case, the type arguments will simply be ignored.
This rule stems from issues of compatibility and principles of substitutability. Since interfaces or superclasses may be generified independently of their subtypes, we may override a generic method with a non-generic one. However, the overriding (non-generic) method must be applicable to calls to the generic method, including calls that explicitly pass type arguments. Otherwise the subtype would not be substitutable for its generified supertype.
If the search does not yield at least one method that is potentially applicable, then a compile-time error occurs.
Let m
be a potentially
applicable method (§15.12.2.1), let e1
, ...,
en
be the actual argument expressions of the method invocation, and
let Ai be the type of ei
(1 ≤ i ≤
n). Then:
If m
is a generic
method, then let F1 ... Fn be the types of the formal
parameters of m
, and let R1 ... Rp (p
≥ 1) be the type parameters of m
, and let
Bl be the declared bound of Rl (1 ≤ l ≤
p). Then:
If the method invocation does
not provide explicit type arguments, then let U1 ... Up be
the type arguments inferred (§15.12.2.7)
for this invocation of m
, using a set of
initial constraints consisting of the constraints Ai
<< Fi (1 ≤ i ≤ n) for each actual
argument expression ei
whose type is a reference
type.
Otherwise, let U1 ... Up be the explicit type arguments given in the method invocation.
Then let Si =
Fi[
R1=U1,...,Rp=Up]
(1 ≤
i ≤ n) be the types inferred for the
formal parameters of m
.
Otherwise, let S1 ... Sn be the
types of the formal parameters of m
.
The
method m
is applicable by
subtyping if and only if both of the following conditions
hold:
If no method applicable by subtyping is found, the search for applicable methods continues with phase 2 (§15.12.2.3).
Otherwise, the most specific method (§15.12.2.5) is chosen among the methods that are applicable by subtyping.
Let m
be a potentially
applicable method (§15.12.2.1), let e1
, ...,
en
be the actual argument expressions of the method invocation, and
let Ai be the type of ei
(1 ≤ i ≤
n). Then:
If m
is a generic
method, then let F1 ... Fn be the types of the formal
parameters of m
, and let R1 ... Rp (p
≥ 1) be the type parameters of m
, and let
Bl be the declared bound of Rl (1 ≤ l ≤
p). Then:
If the method invocation does
not provide explicit type arguments, then let U1 ... Up be
the type arguments inferred (§15.12.2.7)
for this invocation of m
, using a set of
initial constraints consisting of the constraints Ai
<< Fi (1 ≤ i ≤ n).
Otherwise, let U1 ... Up be the explicit type arguments given in the method invocation.
Then let Si =
Fi[
R1=U1,...,Rp=Up]
(1 ≤
i ≤ n) be the types inferred for the
formal parameters of m
.
Otherwise, let S1 ... Sn be the
types of the formal parameters of m
.
The
method m
is applicable by method
invocation conversion if and only if both of the following
conditions hold:
For 1 ≤ i ≤ n, the
type of ei
, Ai, can be converted by method invocation
conversion (§5.3) to Si.
If m
is a generic
method as described above, then Ul <:
Bl[
R1=U1,...,Rp=Up]
(1 ≤ l ≤
p).
If no method applicable by method invocation conversion is found, the search for applicable methods continues with phase 3 (§15.12.2.4).
Otherwise, the most specific method (§15.12.2.5) is chosen among the methods that are applicable by method invocation conversion.
Let m
be a potentially
applicable method (§15.12.2.1) with variable
arity, let e1
, ..., ek
be the actual argument expressions of the
method invocation, and let Ai be the type of ei
(1 ≤ i
≤ k). Then:
If m
is a generic
method, then let F1 ... Fn (1 ≤ n ≤ k+1) be
the types of the formal parameters of m
, where
Fn=T[]
for some type T, and let R1 ... Rp
(p ≥ 1) be the type parameters of m
,
and let Bl be the declared bound of Rl (1 ≤ l ≤
p). Then:
If the method invocation does
not provide explicit type arguments then let U1 ... Up be
the type arguments inferred (§15.12.2.7)
for this invocation of m
, using a set of
initial constraints consisting of the constraints Ai
<< Fi (1 ≤ i < n) and the
constraints Aj << T (n ≤ j ≤
k).
Otherwise let U1 ... Up be the explicit type arguments given in the method invocation.
Then let Si =
Fi[
R1=U1,...,Rp=Up]
(1 ≤
i ≤ n) be the types inferred for the
formal parameters of m
.
Otherwise, let S1 ... Sn (where
n ≤ k+1) be the types of the formal parameters
of m
.
The
method m
is an applicable variable-arity
method if and only if all of the following conditions
hold:
For 1 ≤ i < n, the
type of ei
, Ai, can be converted by method invocation
conversion to Si.
If k ≥ n, then for
n ≤ i ≤ k, the type of ei
, Ai, can be
converted by method invocation conversion to the component type
of Sn.
If k
!= n, or if k = n and An cannot be converted by
method invocation conversion to Sn[]
, then the type
which is the erasure (§4.6) of Sn is
accessible at the point of invocation.
If m
is a generic
method as described above, then Ul <:
Bl[
R1=U1...,Rp=Up]
(1 ≤ l ≤
p).
If no applicable variable arity method is found, a compile-time error occurs.
Otherwise, the most specific method (§15.12.2.5) is chosen among the applicable variable-arity methods.
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
One fixed-arity member
method named m
is more
specific than another member method of the same name and
arity if all of the following conditions hold:
The declared types of the parameters of the first member method are T1, ..., Tn.
The declared types of the parameters of the other method are U1, ..., Un.
If the second
method is generic, then let R1 ... Rp (p ≥ 1) be its
type parameters, let Bl be the declared bound of Rl (1 ≤
l ≤ p), let A1 ... Ap be the type
arguments inferred (§15.12.2.7) for this
invocation under the initial constraints Ti << Ui (1
≤ i ≤ n), and let Si =
Ui[
R1=A1,...,Rp=Ap]
(1 ≤ i ≤
n).
If the second
method is a generic method as described above, then Al <:
Bl[
R1=A1,...,Rp=Ap]
(1 ≤ l ≤
p).
One variable arity member
method named m
is more
specific than another variable arity member method of the
same name if either:
One member method has n parameters and the other has k parameters, where n ≥ k, and:
The types of
the parameters of the first member method are T1, ...,
Tn-1, Tn[]
.
The types of
the parameters of the other method are U1, ..., Uk-1,
Uk[]
.
If the second
method is generic then let R1 ... Rp (p ≥ 1) be
its type parameters, let Bl be the declared bound of Rl (1
≤ l ≤ p), let A1 ... Ap be the type
arguments inferred (§15.12.2.7) for this
invocation under the initial constraints Ti << Ui (1
≤ i ≤ k-1) and Ti << Uk (k
≤ i ≤ n), and let Si =
Ui[
R1=A1,...,Rp=Ap]
(1 ≤ i
≤ k).
If the second
method is a generic method as described above, then Al
<:
Bl[
R1=A1,...,Rp=Ap]
(1
≤ l ≤ p).
One member method has k parameters and the other has n parameters, where n ≥ k, and:
The types of
the parameters of the first method are U1, ..., Uk-1,
Uk[]
.
The types of
the parameters of the other method are T1, ..., Tn-1,
Tn[]
.
If the second
method is generic, then let R1 ... Rp (p ≥ 1) be
its type parameters, let Bl be the declared bound of Rl (1
≤ l ≤ p), let A1 ... Ap be the type
arguments inferred (§15.12.2.7) for this
invocation under the initial constraints Ui << Ti (1
≤ i ≤ k-1) and Uk << Ti (k
≤ i ≤ n), and let Si =
Ti[
R1=A1,...,Rp=Ap] (1 ≤ i ≤
n).
If the second
method is a generic method as described above, then Al
<:
Bl[
R1=A1,...,Rp=Ap]
(1 ≤
l ≤ p).
The above conditions are the only circumstances under which one method may be more specific than another.
A method m1
is strictly more specific than another method
m2
if and only if m1
is more specific than m2
and m2
is not
more specific than m1
.
A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific.
If there is exactly one maximally specific method, then that method is in fact the most specific method; it is necessarily more specific than any other accessible method that is applicable. It is then subjected to some further compile-time checks as described in §15.12.3.
It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:
If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then:
If exactly
one of the maximally specific methods is not declared
abstract
, it is the most specific method.
Otherwise, if
all the maximally specific methods are declared abstract
,
and the signatures of all of the maximally specific methods
have the same erasure (§4.6), then the
most specific method is chosen arbitrarily among the subset of
the maximally specific methods that have the most specific
return type.
However,
the most specific method is considered to throw a checked
exception if and only if that exception or its erasure is
declared in the throws
clauses of each of the maximally
specific methods.
Otherwise, we say that the method invocation is ambiguous, and a compile-time error occurs.
The result type of the chosen method is determined as follows:
If the chosen
method is declared with a return type of
void
, then the result is void
.
Otherwise, if unchecked conversion was necessary for the method to be applicable, then the result type is the erasure (§4.6) of the method's declared return type.
Otherwise, if the chosen method is generic, then for 1 ≤ i ≤ n, let Fi be the formal type parameters of the method, let Ai be the actual type arguments inferred for the method invocation, and let R be the return type of the chosen method.
The result type
is obtained by applying capture conversion
(§5.1.10) to
R[
F1=A1,...,Fn=An]
.
Otherwise, the result type is obtained by applying capture conversion (§5.1.10) to the return type of the chosen method .
The exception types of
the throws
clause of the chosen method are determined as
follows:
If unchecked
conversion was necessary for the method to be applicable, then the
throws
clause is composed of the erasure
(§4.6) of the types in the method's declared
throws
clause.
Otherwise, if the
method being invoked is generic, then for 1 ≤ i ≤
n, let Fi be the type parameters of the method, let Ai be
the type arguments inferred for the method invocation, and let
Ej (1 ≤ j ≤ m) be the exception types declared
in the throws
clause of the method being invoked.
The throws
clause consists of the types
Ej[
F1=A1,...,Fn=An]
.
Otherwise, the type
of the throws
clause is the type given in the method
declaration.
The exception types that a method invocation expression can throw are specified in §11.2.1.
In this section, we describe the process of inferring type arguments for method and constructor invocations. This process is invoked as a subroutine when testing for method (or constructor) applicability (§15.12.2.2, §15.12.2.3, §15.12.2.4).
The process of type inference is inherently complex. Therefore, it is useful to give an informal overview of the process before delving into the detailed specification.
Inference begins with an initial set of constraints. Generally, the constraints require that the statically known types of the actual arguments are acceptable given the declared formal parameter types. We discuss the meaning of "acceptable" below.
Given these initial constraints, one may derive a set of supertype and/or equality constraints on the type parameters of the method or constructor.
Next, one must try and find a solution that satisfies the constraints on the type parameters. As a first approximation, if a type parameter is constrained by an equality constraint, then that constraint gives its solution. Bear in mind that the constraint may equate one type parameter with another, and only when the entire set of constraints on all type variables is resolved will we have a solution.
A supertype constraint T :>
X implies
that the solution is one of supertypes of X. Given several such
constraints on T, we can intersect the sets of supertypes implied by
each of the constraints, since the type parameter must be a member of
all of them. We can then choose the most specific type that is in the
intersection.
Computing the intersection is more complicated than
one might first realize. Given that a type parameter is constrained to
be a supertype of two distinct invocations of a generic type,
say List
and <
Object>
List
, the naive intersection
operation might yield <
String>
Object
. However, a more sophisticated analysis
yields a set containing List
. Similarly,
if a type parameter T is constrained to be a supertype of two
unrelated interfaces I and J, we might infer T must be <
?>
Object
,
or we might obtain a tighter bound of I & J. These issues are
discussed in more detail later in this section.
We use the following notational conventions in this section:
Type expressions are represented using the letters A, F, U, V, and W. The letter A is only used to denote the type of an actual argument, and F is only used to denote the type of a formal parameter.
Arguments to parameterized types are represented using the letters X and Y.
Generic type declarations are represented using the letters G and H.
Inference begins with a set of initial constraints of the form A << F, A = F, or A >> F, where U << V indicates that type U is convertible to type V by method invocation conversion (§5.3), and U >> V indicates that type V is convertible to type U by method invocation conversion.
In a simpler world, the constraints could be of the
form A <:
F - simply requiring that the actual argument
types be subtypes of the formal ones. However, reality is more
involved. As discussed earlier, method applicability testing consists
of up to three phases; this is required for compatibility
reasons. Each phase imposes slightly different constraints. If a
method is applicable by subtyping (§15.12.2.2),
the constraints are indeed subtyping constraints. If a method is
applicable by method invocation conversion
(§15.12.2.3), the constraints imply that the
actual type is convertible to the formal type by method invocation
conversion. The situation is similar for the third phase
(§15.12.2.4), but the exact form of the
constraints differ due to the variable arity.
It is worth noting that a constraint of the form A
= F is never part of the initial constraints. However, it can arise
as the algorithm recurses. We see this occur in the running example
below, when the constraint A << F relates two parameterized
types, as in G<
V>
<< G<
U>
.
A constraint of the form A >> F also
arises when the algorithm recurses, due to the contravariant subtyping
rules associated with lower-bounded wildcards (those of the form
G<
?
super
X>
).
It might be tempting to consider A >> F as being the same as F << A, but the problem of inference is not symmetric. We need to remember which participant in the relation includes a type to be inferred.
These constraints are
then reduced to a set of simpler constraints of the forms T
:>
X, T = X, or T <:
X, where T is a type
parameter of the method. This reduction is achieved by the procedure
given below.
It may be that the initial constraints are unsatisfiable; we say that inference is overconstrained. In that case, we do not necessarily derive unsatisfiable constraints on the type parameters. Instead, we may infer type arguments for the invocation, but once we substitute the type arguments for the type parameters, the applicability test may fail because the actual argument types are not acceptable given the substituted formal parameter types.
An alternative strategy would be to have type inference itself fail in such cases. A Java compiler may choose to do so, provided the effect is equivalent to that specified here.
Given a constraint of the form A << F, A = F, or A >> F:
If F does not involve a type parameter Tj then no constraint is implied on Tj.
Otherwise, F involves a type parameter Tj, and there are four cases to consider.
1. If A is the type of
null
, no constraint is implied on Tj.
2. Otherwise, if the constraint has the form A << F:
If A is a primitive type, then A is converted to a reference type U via boxing conversion and this algorithm is applied recursively to the constraint U << F.
Otherwise, if F =
Tj, then the constraint Tj :>
A is implied.
If F =
U[]
, where the type U involves Tj, then if A is an
array type V[]
, or a type variable with an upper bound
that is an array type V[]
, where V is a reference
type, this algorithm is applied recursively to the constraint V
<< U.
This follows from the covariant subtype relation
among array types. The constraint A << F in this case
means that A << U[]
. A is therefore
necessarily an array type V[]
, or a type variable whose
upper bound is an array type V[]
- otherwise the
relation A << U[]
could never hold true. It
follows that V[]
<< U[]
. Since array
subtyping is covariant, it must be the case that V <<
U.
If F has the form
G<
..., Yk-1, U, Yk+1, ...>
, where U is
a type expression that involves Tj, then if A has a supertype
of the form G<
..., Xk-1, V, Xk+1, ...>
where V is a type expression, this algorithm is applied
recursively to the constraint V = U.
For simplicity, assume that G takes a single type argument. If the method invocation being examined is to be applicable, it must be the case that A is a subtype of some invocation of G. Otherwise, A << F would never be true.
In other words, A << F, where F =
G<
U>
, implies that A <<
G<
V>
for some V. Now, since U is a type
expression (and therefore, U is not a wildcard type argument),
it must be the case that U = V, by the non-variance of
ordinary parameterized type invocations.
The formulation above merely generalizes this reasoning to generics with an arbitrary number of type arguments.
If F has the form
G<
..., Yk-1, ?
extends
U, Yk+1,
...>
, where U involves Tj, then if A has a supertype
that is one of:
G<
...,
Xk-1, V, Xk+1, ...>
, where V is a type
expression. Then this algorithm is applied recursively to the
constraint V << U.
Again, let's keep things as simple as possible, and consider only the case where G has a single type argument.
A << F in this case means A
<< G<
?
extends
U>
. As above, it
must be the case that A is a subtype of some invocation of
G. However, A may now be a subtype of either
G<
V>
, or G<
?
extends
V>
, or G<
?
super
V>
. We
examine these cases in turn. The first variation is described
(generalized to multiple arguments) by the sub-bullet directly
above. We therefore have A = G<
V>
<<
G<
?
extends
U>
. The rules of
subtyping for wildcards imply that V << U.
G<
...,
Xk-1, ?
extends
V, Xk+1,
...>
. Then this algorithm is applied recursively to the
constraint V << U.
Extending the analysis above, we have A =
G<
?
extends
V>
<<
G<
?
extends
U>
. The rules of
subtyping for wildcards again imply that V <<
U.
Otherwise, no constraint is implied on Tj.
Here, we have A = G<
?
super
V>
<< G<
?
extends
U>
. In general, we cannot conclude anything in this
case. However, it is not necessarily an error. It may be that
U will eventually be inferred to be Object
, in which case
the call may indeed be valid. Therefore, we simply refrain
from placing any constraint on U.
If F has the form
G<
..., Yk-1, ?
super
U, Yk+1,
...>
, where U involves Tj, then if A has a supertype
that is one of:
G<
...,
Xk-1, V, Xk+1, ...>
. Then this algorithm is
applied recursively to the constraint V >> U.
As usual, we consider only the case where G has a single type argument.
A << F in this case means A
<< G<
?
super
U>
. As above, it
must be the case that A is a subtype of some invocation of
G. A may now be a subtype of either
G<
V>
, or G<
?
extends
V>
, or G<
?
super
V>
. We
examine these cases in turn. The first variation is described
(generalized to multiple arguments) by the sub-bullet directly
above. We therefore have A = G<
V>
<<
G<
?
super
U>
. The rules of subtyping
for wildcards imply that V >> U.
G<
..., Xk-1, ?
super
V, Xk+1, ...>
. Then this algorithm is
applied recursively to the constraint V >> U.
We have A = G<
?
super
V>
<< G<
?
super
U>
. The rules of subtyping for lower-bounded
wildcards again imply that V >> U.
Otherwise, no constraint is implied on Tj.
Here, we have A = G<
?
extends
V>
<< G<
?
super
U>
. In general, we cannot conclude anything in this
case. However, it is not necessarily an error. It may be that
U will eventually be inferred to the null type, in which
case the call may indeed be valid. Therefore, we simply
refrain from placing any constraint on U.
3. Otherwise, if the constraint has the form A = F:
If F =
U[]
where the type U involves Tj, then if A is an
array type V[]
, or a type variable with an upper bound
that is an array type V[]
, where V is a reference
type, this algorithm is applied recursively to the constraint V
= U.
If the array types U[]
and
V[]
are the same, their component types must be the
same.
If F has the form
G<
..., Yk-1, U, Yk+1, ...>
, where U is
type expression that involves Tj, then if A is of the form
G<
..., Xk-1, V, Xk+1,...>
where V is a
type expression, this algorithm is applied recursively to the
constraint V = U.
If F has the form
G<
..., Yk-1, ?
extends
U, Yk+1,
...>
, where U involves Tj, then if A is one
of:
If F has the form
G<
..., Yk-1, ?
super
U, Yk+1
,...>
, where U involves Tj, then if A is one
of:
4. Otherwise, if the constraint has the form A >> F:
If F = Tj, then
the constraint Tj <:
A is implied.
We do not make use of such constraints in the main body of the inference algorithm. However, they are used in §15.12.2.8.
If F =
U[]
, where the type U involves Tj, then if A is an
array type V[]
, or a type variable with an upper bound
that is an array type V[]
, where V is a reference
type, this algorithm is applied recursively to the constraint V
>> U. Otherwise, no constraint is implied on Tj.
This follows from the covariant subtype relation
among array types. The constraint A >> F in this case
means that A >> U[]
. A is therefore
necessarily an array type V[]
, or a type variable whose
upper bound is an array type V[]
- otherwise the
relation A >> U[]
could never hold true. It
follows that V[]
>> U[]
. Since array
subtyping is covariant, it must be the case that V >>
U.
If F has the form
G<
..., Yk-1, U, Yk+1, ...>
, where U is a type
expression that involves Tj, then:
If A is an instance of a non-generic type, then no constraint is implied on Tj.
In this case (once again restricting the
analysis to the unary case), we have the constraint A
>> F = G<
U>
. A must be a supertype of the
generic type G. However, since A is not a parameterized
type, it cannot depend upon the type argument U in any
way. It is a supertype of G<
X>
for every X that is
a valid type argument to G. No meaningful constraint on U
can be derived from A.
If A is an invocation of a generic type declaration H, where H is either G or superclass or superinterface of G, then:
If H
≠ G, then let S1, ..., Sn be the type parameters
of G, and let H<
U1, ..., Ul>
be the unique
invocation of H that is a supertype of G<
S1, ...,
Sn>
, and let V = H<
U1, ...,
Ul>
[
Sk=U]
. Then, if V :>
F this algorithm is applied recursively to the
constraint A >> V.
Our goal here is to simplify the relationship between A and F. We aim to recursively invoke the algorithm on a simpler case, where the type argument is known to be an invocation of the same generic type declaration as the formal.
Let's consider the case where both H
and G have only a single type argument. Since we have
the constraint A = H<
X>
>> F =
G<
U>
, where H is distinct from G, it must be
the case that H is some proper superclass or
superinterface of G. There must be a (non-wildcard)
invocation of H that is a supertype of F =
G<
U>
. Call this invocation V.
If we replace F by V in the constraint, we will have accomplished the goal of relating two invocations of the same generic (as it happens, H).
How do we compute V? The declaration
of G must introduce a type parameter S, and there must
be some (non-wildcard) invocation of H, H<
U1>
,
that is a supertype of G<
S>
. Substituting the
type expression U for S will then yield a
(non-wildcard) invocation of H,
H<
U1>
[
S=U]
,
that is a supertype of G<
U>
. For example, in the
simplest instance, U1 might be S, in which case we
have G<
S>
<:
H<
S>
, and
G<
U>
<:
H<
U>
=
H<
S>
[
S=U]
= V.
It may be the case that H<
U1>
is independent of S - that is, S does not occur in
U1 at all. However, the substitution described above is
still valid - in this situation, V =
H<
U1>
[
S=U]
= H<
U1>
. Furthermore, in this circumstance,
G<
T>
<:
H<
U1>
for any T, and
in particular G<
U>
<:
H<
U1>
=
V.
Regardless of whether U1 depends on
S, we have determined the type V, the invocation of
H that is a supertype of G<
U>
. We can now
invoke the algorithm recursively on the constraint
H<
X>
= A >> V =
H<
U1>
[
S=U]
. We
will then be able to relate the type arguments of both
invocations of H and extract the relevant constraints
from them.
Otherwise, if A is of
the form G<
..., Xk-1, W, Xk+1, ...>
,
where W is a type expression, this algorithm is applied
recursively to the constraint W = U.
We have A = G<
W>
>>
F = G<
U>
for some type expression W. Since
W is a type expression (and not a wildcard type
argument), it must be the case that W = U, by the
invariance of parameterized types.
Otherwise, if A is of
the form G<
..., Xk-1, ?
extends
W, Xk+1, ...>
, this algorithm is applied
recursively to the constraint W >> U.
We have A = G<
?
extends
W>
>> F =
G<
U>
for some type expression W. It
must be the case that W >> U, by the subtyping
rules for wildcard types.
Otherwise, if A is of
the form G<
..., Xk-1, ?
super
W,
Xk+1, ...>
, this algorithm is applied
recursively to the constraint W << U.
We have A = G<
?
super
W>
>> F = G<
U>
for some
type expression W. It must be the case that W <<
U, by the subtyping rules for wildcard types.
If F has the form
G<
..., Yk-1, ?
extends
U, Yk+1,
...>
, where U is a type expression that involves Tj,
then:
If A is an instance of a non-generic type, then no constraint is implied on Tj.
Once again restricting the analysis to the
unary case, we have the constraint A >> F =
G<
?
extends
U>
. A must be a
supertype of the generic type G. However, since A is not a
parameterized type, it cannot depend upon U in any way. It
is a supertype of the type G<
?
extends
X>
for every X such that ?
extends
X is a valid type argument to G. No meaningful constraint
on U can be derived from A.
If A is an invocation of a generic type declaration H, where H is either G or superclass or superinterface of G, then:
If H
≠ G, then let S1, ..., Sn be the type parameters
of G, and let H<
U1, ..., Ul>
be the unique
invocation of H that is a supertype of G<
S1, ...,
Sn>
, and let V = H<
?
extends
U1, ..., ?
extends
Ul>
[
Sk=U]
. Then this algorithm is
applied recursively to the constraint A >>
V.
Our goal here is once more to simplify the relationship between A and F, and recursively invoke the algorithm on a simpler case, where the type argument is known to be an invocation of the same generic type as the formal.
Assume both H and G have only a
single type argument. Since we have the constraint A =
H<
X>
>> F = G<
?
extends
U>
, where H is distinct from G, it
must be the case that H is some proper superclass or
superinterface of G. There must be an invocation of
H<
Y>
, such that H<
X>
>> H<
Y>
, that we can use instead
of F = G<
?
extends
U>
.
How do we compute
H<
Y>
? As before, note that the
declaration of G must introduce a type parameter S,
and there must be some (non-wildcard) invocation of H,
H<
U1>
, that is a supertype of
G<
S>
. However, substituting ?
extends
U for S is not generally valid. To see this,
assume U1 = T[]
.
Instead, we produce an invocation of
H, H<
?
extends
U1>
[
S=U]
. In the simplest
instance, U1 might be S, in which case we have
G<
S>
<:
H<
S>
,
and G<
?
extends
U>
<:
H<
?
extends
U>
=
H<
?
extends
S>
[
S=U]
= V.
Otherwise, if A is of
the form G<
..., Xk-1, ?
extends
W, Xk+1, ...>
, this algorithm is applied
recursively to the constraint W >> U.
We have A = G<
?
extends
W>
>> F = G<
?
extends
U>
for some type expression W. By the
subtyping rules for wildcards it must be the case that W
>> U.
If F has the form
G<
..., Yk-1, ?
super
U, Yk+1,
...>
, where U is a type expression that involves Tj,
then A is either:
If A is an instance of a non-generic type, then no constraint is implied on Tj.
Restricting the analysis to the unary case,
we have the constraint A >> F = G<
?
super
U>
. A must be a supertype of the generic
type G. However, since A is not a parameterized type, it
cannot depend upon U in any way. It is a supertype of the
type G<
?
super
X>
for every X such
that ?
super
X is a valid type argument to G. No
meaningful constraint on U can be derived from A.
If A is an invocation of a generic type declaration H, where H is either G or superclass or superinterface of G, then:
If H
≠ G, then let S1, ..., Sn be the type parameters
of G, and let H<
U1, ..., Ul>
be the
unique invocation of H that is a supertype of
G<
S1, ..., Sn>
, and let V =
H<
?
super
U1, ..., ?
super
Ul>
[
Sk=U]
. Then this algorithm
is applied recursively to the constraint A >>
V.
The treatment here is analogous to the
case where A = G<
?
extends
U>
. Here our example would produce an invocation
H<
?
super
U1>
[
S=U]
.
Otherwise, if A is of
the form G<
..., Xk-1, ?
super
W, ..., Xk+1, ...>
, this algorithm is applied
recursively to the constraint W << U.
We have A = G<
?
super
W>
>> F = G<
?
super
U>
for some type expression W. It must be the
case that W << U, by the subtyping rules for
wildcard types.
This concludes the process of determining constraints on the type parameters of a method.
Note that this process does not impose any constraints on the type parameters based on their declared bounds. Once type arguments are inferred, they will be tested against the declared bounds of the type parameters as part of applicability testing.
Note also that type inference does not affect soundness in any way. If the types inferred are nonsensical, the invocation will yield a type error. The type inference algorithm should be viewed as a heuristic, designed to perform well in practice. If it fails to infer the desired result, explicit type paramneters may be used instead.
Next, for each type variable Tj (1 ≤ j ≤ n), the implied equality constraints are resolved as follows.
For each implied equality constraint Tj = U or U = Tj:
If U is not one of the type parameters of the method, then U is the type inferred for Tj. Then all remaining constraints involving Tj are rewritten such that Tj is replaced with U. There are necessarily no further equality constraints involving Tj, and processing continues with the next type parameter, if any.
Otherwise, if U is Tj, then this constraint carries no information and may be discarded.
Otherwise, the constraint is of the form Tj = Tk for j ≠ k. Then all constraints involving Tj are rewritten such that Tj is replaced with Tk, and processing continues with the next type variable.
Then, for each remaining
type variable Tj, the constraints Tj :>
U are
considered. Given that these constraints are Tj :>
U1
... Tj :>
Uk, the type of Tj is inferred as lub(U1
... Uk), computed as follows:
For a type U, we write ST(U) for the set of supertypes of U, and define the erased supertype set of U:
EST(U) = { V | W in ST(U) and V = |W| } where |W| is the erasure (§4.6) of W.
The reason for computing the set of erased supertypes is to deal with situations where a type variable is constrained to be a supertype of several distinct invocations of a generic type declaration.
For example, if T
:>
List
and T
<
String>
:>
List
, simply intersecting
the sets ST(<
Object>
List
) =
{ <
String>
List
, <
String>
Collection
,
<
String>
Object
} and ST(List
) =
{ <
Object>
List
, <
Object>
Collection
,
<
Object>
Object
} would yield a set { Object
}, and we would have lost
track of the fact that T can safely be assumed to be
a List
.
In contrast, intersecting
EST(List
) =
{ <
String>
List
, Collection
, Object
}
and EST(List
) =
{ <
Object>
List
, Collection
, Object
}
yields { List
, Collection
,
Object
}, which we will eventually enable us to infer T
= List
as described below.
<
?>
The erased candidate set for type parameter Tj, EC, is the intersection of all the sets EST(U) for each U in U1 ... Uk.
The minimal erased candidate set for Tj is:
MEC = { V | V in EC, and for all W ≠ V in EC, it is not the case that W <:
V }
Because we are seeking to infer more precise types, we wish to filter out any candidates that are supertypes of other candidates. This is what computing MEC accomplishes.
In our running example, we had EC =
{ List
, Collection
, Object
},
and now MEC = { List
}.
The next step will be to recover type arguments for the inferred types themselves.
For any element G of MEC that is a generic type declaration, define the relevant invocations of G, Inv(G), to be:
Inv(G) = { V | 1 ≤ i ≤ k: V in ST(Ui), V = G<
...>
}
In our running example, the only generic element of
MEC is List
, and Inv(List
) =
{ List
, <
String>
List
}. We now will seek to find a type argument
for <
Object>
List
that contains
(§4.5.1) both String
and Object
.
This is done by means of the least containing
invocation (lci) operation defined below. The first line defines lci()
on a set, such as Inv(List
), as an operation on a
list of the elements of the set. The next line defines the operation
on such lists, as a pairwise reduction on the elements of the
list. The third line is the definition of lci() on pairs of
parameterized types, which in turn relies on the notion of least
containing type argument (lcta).
lcta() is defined for all six possible cases. Then CandidateInvocation(G) defines the most specific invocation of the generic type G that contains all the invocations of G that are known to be supertypes of Tj. This will be our candidate invocation of G in the bound we infer for Tj.
Define CandidateInvocation(G) = lci(Inv(G)), where lci, the least containing invocation, is defined:
where lcta() is the least containing type argument function defined (assuming U and V are type expressions) as:
where glb() is as defined in §5.1.10.
Finally, we define a bound for Tj based on on all the elements of the minimal erased candidate set of its supertypes. If any of these elements are generic, we use the CandidateInvocation() function to recover the type argument information.
Define Candidate(W) = CandidateInvocation(W) if W is generic, W otherwise.
The inferred type for
Tj, lub(U1 ... Uk), is Candidate(W1) &
... &
Candidate(Wr), where Wi (1 ≤ i ≤
r) are the elements of MEC.
It is possible that the process above yields an infinite type. This is permissible, and a Java compiler must recognize such situations and represent them appropriately using cyclic data structures.
The possibility of an infinite type stems from the recursive calls to lub(). Readers familiar with recursive types should note that an infinite type is not the same as a recursive type.
If any of the method's type arguments were not inferred from the types of the actual arguments, they are now inferred as follows.
If the method
result occurs in a context where it will be subject to assignment
conversion (§5.2) to a type S, then let R
be the declared result type of the method, and let R' =
R[
T1=B(T1) ... Tn=B(Tn)]
, where B(Ti) is
the type inferred for Ti in the previous section or Ti if no
type was inferred.
If S is a reference type, then let S' be S. Otherwise, if S is a primitive type, then let S' be the result of applying boxing conversion (§5.1.7) to S.
Then, a set of initial constraints consisting of:
additional
constraints Bi[
T1=B(T1) ... Tn=B(Tn)]
>> Ti, where Bi is the declared bound of
Ti,
additional constraints B(Ti) << Bi[T1=B(T1) ... Tn=B(Tn)], where Bi is the declared bound of Ti,
for any constraint of the form V >> Ti generated in §15.12.2.7: a constraint V[T1=B(T1) ... Tn=B(Tn)] >> Ti.
for any constraint of the form Ti = V generated in §15.12.2.7: a constraint Ti = V[T1=B(T1) ... Tn=B(Tn)].
is created and used to infer constraints on the type arguments using the algorithm of §15.12.2.7.
Any equality
constraints are resolved, and then, for each remaining constraint
of the form Ti <:
Uk, the argument Ti is inferred to
be glb(U1, ..., Uk) (§5.1.10).
If Ti appears as a type argument in any Uk, then Ti is inferred to be a type variable X whose upper bound is the parameterized type given by glb(U1[Ti=X], ..., Uk[Ti=X]) and whose lower bound is the null type.
Any
remaining type variable T that has not yet
been inferred is then inferred to have type Object
. If
a previously inferred type variable P uses T, then P is
inferred to be P[T=Object
].
Otherwise, the
unresolved type arguments are inferred by invoking the procedure
described in this section under the assumption that the method
result was assigned to a variable of type Object
.
If there is a most specific method declaration for a method invocation, it is called the compile-time declaration for the method invocation.
If the method invocation has, before the left parenthesis, a MethodName of the form Identifier, and the compile-time declaration is an instance method, then:
If the method invocation occurs in a static context (§8.1.3), then a compile-time error occurs.
The reason is that a method invocation of this
form cannot be used to invoke an instance method in places where
this
(§15.8.3) is not defined.
Otherwise, let C be the innermost enclosing class of which the compile-time declaration is a member.
If the invocation is not directly enclosed by C or an inner class of C, then a compile-time error occurs.
If the
method invocation has, before the left parenthesis,
a MethodName of the
form TypeName .
Identifier, or if the
method invocation, before the left parenthesis, has the
form TypeName
.
NonWildTypeArguments Identifier, then the
compile-time declaration must be static
, or a compile-time error
occurs.
The reason is that a method invocation of this form does not specify a reference to an object that can serve as this within the instance method.
If the
method invocation has, before the left parenthesis, the form super
.
NonWildTypeArgumentsopt Identifier,
then:
If the
method invocation has, before the left parenthesis, the
form ClassName .
super
.
NonWildTypeArgumentsopt Identifier,
then:
If
the compile-time declaration is abstract
, a compile-time error
occurs.
If the method invocation occurs in a static context, a compile-time error occurs.
Otherwise, let C be the class denoted by ClassName.
If the invocation is not directly enclosed by C or an inner class of C, then a compile-time error occurs.
If the
compile-time declaration is void
, then the method invocation must be
a top level expression (that is, the Expression in an expression
statement (§14.8) or in
the ForInit or ForUpdate
part of a for
statement (§14.14)), or a
compile-time error occurs.
The reason is that such a method invocation produces no value and so must be used only in a situation where a value is not needed.
A method is signature polymorphic if and only if all of the following conditions hold:
It
takes a single variable arity parameter
(§8.4.1) whose declared type is
Object
[]
.
In Java SE 7, the only signature polymorphic methods
are the invoke
and invokeExact
methods of the class
java.lang.invoke.MethodHandle
.
If the compile-time declaration for the method invocation is not a signature polymorphic method, then the types of its parameters are the types of its formal parameters, and the result type is its chosen result type.
Otherwise, if the compile-time declaration for the method invocation is a signature polymorphic method, then:
The types of its parameters are the static types of the actual argument expressions.
An argument expression which is the
null literal null
(§3.10.7) is treated as
having the static type Void
.
The result type is determined as follows:
If the method invocation
expression is an expression statement, the method is
void
.
Otherwise, if the method invocation expression is the operand of a cast expression (§15.16), the return type is the erasure (§4.6) of the type of the cast expression.
Otherwise, the return type is
the method's declared return type, Object
.
The following compile-time information is then associated with the method invocation for use at run time:
The qualifying type of the method invocation (§13.1).
The number of parameters and the types of the parameters, in order.
The invocation mode, computed as follows:
If the compile-time declaration
has the static
modifier, then the invocation mode is
static
.
Otherwise, if the compile-time
declaration has the private
modifier, then the invocation
mode is nonvirtual
.
Otherwise, if the part of the
method invocation before the left parenthesis is of the form
super
.
Identifier or of the
form ClassName .
super
.
Identifier, then the invocation mode is super
.
Otherwise, if the compile-time
declaration is in an interface, then the invocation mode is
interface
.
If the
compile-time declaration for the method invocation is not void
, then
the type of the method invocation expression is the result type
specified in the compile-time declaration.
At run time, method invocation requires five steps. First, a target reference may be computed. Second, the argument expressions are evaluated. Third, the accessibility of the method to be invoked is checked. Fourth, the actual code for the method to be executed is located. Fifth, a new activation frame is created, synchronization is performed if necessary, and control is transferred to the method code.
There are several cases to consider, depending on which of the five productions for MethodInvocation (§15.12) is involved:
If the first production for MethodInvocation, which includes a MethodName, is involved, then there are three subcases:
If the MethodName is a simple name, that is, just an Identifier, then there are two subcases:
If the
invocation mode is static
, then there is no target
reference.
Otherwise, let T be the
enclosing type declaration of which the method is a
member, and let n be an integer such that T is the
n'th lexically enclosing type declaration
(§8.1.3) of the class whose
declaration immediately contains the method
invocation. Then the target reference is the n'th
lexically enclosing instance (§8.1.3)
of this
.
It is a compile-time
error if the n'th lexically enclosing instance
(§8.1.3) of this
does not
exist.
If
the MethodName is a qualified name of the
form TypeName .
Identifier, then
there is no target reference.
If
the MethodName is a qualified name of the
form FieldName .
Identifier, then
there are two subcases:
If the second production for MethodInvocation, which includes a Primary, is involved, then there are two subcases:
In either case, if the evaluation of the Primary expression completes abruptly, then no part of any argument expression appears to have been evaluated, and the method invocation completes abruptly for the same reason.
If the third
production for MethodInvocation, which
includes the keyword super
, is involved, then the target
reference is the value of this
.
If the fourth
production
for MethodInvocation, ClassName
.
super
, is involved, then the target reference is the value
of ClassName.
this
.
If the fifth
production for MethodInvocation, beginning
with TypeName
.
NonWildTypeArguments, is involved, then
there is no target reference.
Example 15.12.4.1-1. Target References and static
Methods
When a target reference is computed and then
discarded because the invocation mode is static
, the reference is
not examined to see whether it is null
:
class Test1 { static void mountain() { System.out.println("Monadnock"); } static Test1 favorite(){ System.out.print("Mount "); return null; } public static void main(String[] args) { favorite().mountain(); } }
which prints:
Mount Monadnock
Here favorite()
returns null
,
yet no NullPointerException
is thrown.
Example 15.12.4.1-2. Evaluation Order During Method Invocation
As part of an instance method invocation (§15.12), there is an expression that denotes the object to be invoked. This expression appears to be fully evaluated before any part of any argument expression to the method invocation is evaluated.
So, for example, in:
class Test2 { public static void main(String[] args) { String s = "one"; if (s.startsWith(s = "two")) System.out.println("oops"); } }
the occurrence of s
before
".startsWith
" is evaluated first, before the
argument expression s = "two"
. Therefore, a
reference to the string "one"
is remembered as the
target reference before the local variable s
is
changed to refer to the string "two"
. As a result,
the startsWith
method is invoked for target
object "one"
with
argument "two"
, so the result of the invocation is
false
, as the string "one"
does not start
with "two"
. It follows that the test program does
not print "oops
".
The process of evaluating the argument list differs, depending on whether the method being invoked is a fixed arity method or a variable arity method (§8.4.1).
If the method being
invoked is a variable arity method m
, it
necessarily has n > 0 formal parameters. The final formal
parameter of m
necessarily has type T[]
for some T, and m
is necessarily being invoked
with k ≥ 0 actual argument expressions.
If m
is being
invoked with k ≠ n actual argument expressions, or, if
m
is being invoked with k = n actual argument expressions
and the type of the k'th argument expression is not assignment
compatible with T[]
, then the argument list (e1
, ...,
en-1
, en
, ..., ek
) is evaluated as if it were written as (e1
,
..., en-1
, new
|T[]
| {
en
, ..., ek
}
),
where |T[]
| denotes the erasure (§4.6)
of T[]
.
The preceding paragraphs are crafted to handle the
interaction of parameterized types and array types that occurs in a
Java Virtual Machine with erased generics. Namely, if the element type T of the
variable array parameter is non-reifiable,
e.g. List
, then special care must be
taken with the array creation expression (§15.10)
because the created array's element type must be reifiable. By erasing
the array type of the final expression in the argument list, we are
guaranteed to obtain a reifiable element type. Then, since the array
creation expression appears in a method invocation context, an
unchecked conversion (§5.1.9) is possible from
the array type with reifiable element type to an array type with
non-reifiable element type, specifically that of the variable arity
parameter. A Java compiler is required by §5.1.9
to give a compile-time unchecked warning at this conversion. Oracle's
reference implementation of a Java compiler identifies the unchecked
warning here as a more informative unchecked generic array
creation.
<
String>
The argument expressions (possibly rewritten as described above) are now evaluated to yield argument values. Each argument value corresponds to exactly one of the method's n formal parameters.
The argument expressions, if any, are evaluated in order, from left to right. If the evaluation of any argument expression completes abruptly, then no part of any argument expression to its right appears to have been evaluated, and the method invocation completes abruptly for the same reason. The result of evaluating the j'th argument expression is the j'th argument value, for 1 ≤ j ≤ n. Evaluation then continues, using the argument values, as described below.
Let C be the class
containing the method invocation, and let T be the qualifying type
of the method invocation (§13.1), and let m
be
the name of the method as determined at compile time
(§15.12.3).
An
implementation of the Java programming language must ensure, as part of linkage, that
the method m
still exists in the type T. If this
is not true, then a NoSuchMethodError
(which is a subclass of IncompatibleClassChangeError
)
occurs.
If
the invocation mode is interface
, then the implementation must also
check that the target reference type still implements the specified
interface. If the target reference type does not still implement the
interface, then an IncompatibleClassChangeError
occurs.
The
implementation must also ensure, during linkage, that the type T and
the method m
are accessible:
If
m
is public
, then m
is accessible. (All members of interfaces are public
(§9.2).)
If
m
is protected
,
then m
is accessible if and only if
either T is in the same package as C, or C is T or a
subclass of T.
If
m
has default (package) access,
then m
is accessible if and only if T
is in the same package as C.
If
m
is private
, then m
is accessible if and only if C is T, or C encloses
T, or T encloses C, or T and C are both enclosed
by a third class.
If either T
or m
is not accessible, then an IllegalAccessError
occurs
(§12.3).
The strategy for method lookup depends on the invocation mode.
If
the invocation mode is static
, no target reference is needed and
overriding is not allowed. Method m
of class T is the one to be
invoked.
Otherwise, an instance method is to be
invoked and there is a target reference. If the target reference is
null
, a NullPointerException
is thrown at this point. Otherwise, the target
reference is said to refer to a target object and
will be used as the value of the keyword this
in the invoked
method. The other four possibilities for the invocation mode are then
considered.
If
the invocation mode is nonvirtual
, overriding is not allowed. Method
m
of class T is the one to be invoked.
Otherwise, if the
invocation mode is virtual
, and T and m
jointly indicate a
signature polymorphic method (§15.12.3), then the
target object is an instance of java.lang.invoke.MethodHandle
. The method handle
encapsulates a type which is matched against the
information associated with the method invocation at compile time
(§15.12.3). Details of this matching are given in
The Java Virtual Machine Specification, Java SE 7 Edition and the Java SE platform API. If matching succeeds,
the target method encapsulated by the method
handle is directly and immediately invoked,
and the procedure in §15.12.4.5 is not
executed.
Otherwise, the invocation mode is
interface
, virtual
, or super
, and overriding may
occur. A dynamic method lookup is used. The
dynamic lookup process starts from a class S, determined as
follows:
If the invocation mode is interface
or virtual
, then S is initially the actual run-time class R
of the target object.
This is true even if the target object is an
array instance. (Note that for invocation mode interface
, R
necessarily implements T; for invocation mode virtual
, R is
necessarily either T or a subclass of T.)
If the invocation mode is super
,
then S is initially the qualifying type
(§13.1) of the method invocation.
The
dynamic method lookup uses the following procedure to search class
S, and then the superclasses of class S, as necessary, for
method m
.
Let X be the compile-time type of the target reference of the method invocation. Then:
If class S contains a declaration
for a non-abstract
method named m
with the
same descriptor (same number of parameters, the same parameter
types, and the same return type) required by the method invocation
as determined at compile time (§15.12.3),
then:
If the invocation mode is
super
or interface
, then this is the method to be invoked,
and the procedure terminates.
If the invocation mode is
virtual
, and the declaration in S overrides
(§8.4.8.1) X.
m
,
then the method declared in S is the method to be invoked,
and the procedure terminates.
If the invocation mode is
virtual
, and the declaration in S does not override
X.
m
, and moreover X.
m
is declared
abstract
, then an AbstractMethodError
is thrown.
Otherwise, if S has a superclass, this same lookup procedure is performed recursively using the direct superclass of S in place of S; the method to be invoked is the result of the recursive invocation of this lookup procedure.
The above
procedure (if it terminates without error)
will find a non-abstract
, accessible method to invoke,
provided that all classes and interfaces in the program have been
consistently compiled. However, if this is not the case, then various
errors may occur. The specification of the behavior of a Java Virtual Machine under
these circumstances is given by The Java Virtual Machine Specification, Java SE 7 Edition.
We note that the dynamic lookup process, while described here explicitly, will often be implemented implicitly, for example as a side-effect of the construction and use of per-class method dispatch tables, or the construction of other per-class structures used for efficient dispatch.
Example 15.12.4.4-1. Overriding and Method Invocation
class Point { final int EDGE = 20; int x, y; void move(int dx, int dy) { x += dx; y += dy; if (Math.abs(x) >= EDGE || Math.abs(y) >= EDGE) clear(); } void clear() { System.out.println("\tPoint clear"); x = 0; y = 0; } } class ColoredPoint extends Point { int color; void clear() { System.out.println("\tColoredPoint clear"); super.clear(); color = 0; } }
Here, the subclass ColoredPoint
extends the clear
abstraction defined by its
superclass Point
. It does so by overriding
the clear
method with its own method, which invokes
the clear
method of its superclass, using the
form super.clear()
.
This method is then invoked whenever the target
object for an invocation of clear
is
a ColoredPoint
. Even the
method move
in Point
invokes
the clear
method of
class ColoredPoint
when the class of this
is ColoredPoint
, as shown by the output of this
test program:
class Test1 { public static void main(String[] args) { Point p = new Point(); System.out.println("p.move(20,20):"); p.move(20, 20); ColoredPoint cp = new ColoredPoint(); System.out.println("cp.move(20,20):"); cp.move(20, 20); p = new ColoredPoint(); System.out.println("p.move(20,20), p colored:"); p.move(20, 20); } }
which is:
p.move(20,20): Point clear cp.move(20,20): ColoredPoint clear Point clear p.move(20,20), p colored: ColoredPoint clear Point clear
Overriding is sometimes called "late-bound
self-reference"; in this example it means that the reference
to clear
in the body
of Point.move
(which is really syntactic shorthand
for this.clear
) invokes a method chosen "late" (at
run time, based on the run-time class of the object referenced by
this
) rather than a method chosen "early" (at compile time, based
only on the type of this
). This provides the programmer a powerful
way of extending abstractions and is a key idea in object-oriented
programming.
Example 15.12.4.4-2. Method Invocation Using super
An overridden instance method of a superclass may be
accessed by using the keyword super
to access the members of the
immediate superclass, bypassing any overriding declaration in the
class that contains the method invocation.
When accessing an instance variable, super
means
the same as a cast of this
(§15.11.2), but this
equivalence does not hold true for method invocation. This is
demonstrated by the example:
class T1 { String s() { return "1"; } } class T2 extends T1 { String s() { return "2"; } } class T3 extends T2 { String s() { return "3"; } void test() { System.out.println("s()=\t\t" + s()); System.out.println("super.s()=\t" + super.s()); System.out.println("((T2)this).s()=\t" + ((T2)this).s()); System.out.println("((T1)this).s()=\t" + ((T1)this).s()); } } class Test2 { public static void main(String[] args) { T3 t3 = new T3(); t3.test(); } }
which produces the output:
s()= 3 super.s()= 2 ((T2)this).s()= 3 ((T1)this).s()= 3
The casts to types T1
and T2
do not change the method that is invoked,
because the instance method to be invoked is chosen according to the
run-time class of the object referred to by this
. A cast does not
change the class of an object; it only checks that the class is
compatible with the specified type.
A
method m
in some class S has been identified as the one to be
invoked.
Now a
new activation frame is created, containing the
target reference (if any) and the argument values (if any), as well as
enough space for the local variables and stack for the method to be
invoked and any other bookkeeping information that may be required by
the implementation (stack pointer, program counter, reference to
previous activation frame, and the like). If there is not sufficient
memory available to create such an activation frame, a StackOverflowError
is
thrown.
The
newly created activation frame becomes the current activation
frame. The effect of this is to assign the argument values to
corresponding freshly created parameter variables of the method, and
to make the target reference available as this
, if there is a target
reference. Before each argument value is assigned to its corresponding
parameter variable, it is subjected to method invocation conversion
(§5.3), which includes any required value set
conversion (§5.1.13).
If
the erasure (§4.6) of the type of the method
being invoked differs in its signature from the erasure of the type of
the compile-time declaration for the method invocation
(§15.12.3), then if any of the argument values is
an object which is not an instance of a subclass or subinterface of
the erasure of the corresponding formal parameter type in the
compile-time declaration for the method invocation, then a ClassCastException
is
thrown.
If the method m
is a
native
method but the necessary native, implementation-dependent
binary code has not been loaded or otherwise cannot be dynamically
linked, then an UnsatisfiedLinkError
is thrown.
If the method m
is not
synchronized
, control is transferred to the body of the method m
to be invoked.
If the method m
is
synchronized
, then an object must be locked before the transfer of
control. No further progress can be made until the current thread can
obtain the lock. If there is a target reference, then the target
object must be locked; otherwise the Class
object for class S, the
class of the method m
, must be locked. Control is then transferred
to the body of the method m
to be invoked. The object is
automatically unlocked when execution of the body of the method has
completed, whether normally or abruptly. The locking and unlocking
behavior is exactly as if the body of the method were embedded in a
synchronized
statement (§14.19).
Example 15.12.4.5-1. Invoked Method Signature Has Different Erasure Than Compile-Time Method Signature
Consider the declarations:
abstract class C<T> { abstract T id(T x); } class D extends C<String> { String id(String x) { return x; } }
Now, given an invocation:
C c = new D(); c.id(new Object()); // fails with a ClassCastException
The erasure of the actual method being
invoked, D.id()
, differs in its signature from that
of the compile-time method declaration, C.id()
. The
former takes an argument of type String
while the latter takes an
argument of type Object
. The invocation fails with a ClassCastException
before
the body of the method is executed.
Such situations can only arise if the program gives rise to a compile-time unchecked warning (§4.8, §5.1.9, §5.5.2, §8.4.1, §8.4.8.3, §8.4.8.4, §9.4.1.2, §15.12.4.2).
Implementations can enforce these semantics by
creating bridge methods. In the above example,
the following bridge method would be created in
class D
:
Object id(Object x) { return id((String) x); }
This is the method that would actually be invoked by
the Java Virtual Machine in response to the call c.id(new
Object())
shown above, and it will execute the cast and
fail, as required.
An array access expression refers to a variable that is a component of an array.
ArrayAccess:
ExpressionName [
Expression ]
PrimaryNoNewArray [
Expression ]
An array access expression contains two subexpressions, the array reference expression (before the left bracket) and the index expression (within the brackets).
Note that the array reference expression may be a name or any primary expression that is not an array creation expression (§15.10).
The type of
the array reference expression must be an array type (call it
T[]
, an array whose components are of type T), or a
compile-time error occurs.
The index
expression undergoes unary numeric promotion
(§5.6.1). The promoted type must be int
, or a
compile-time error occurs.
The type of the array access expression is the result of applying capture conversion (§5.1.10) to T.
The result of an array access expression is a variable of type T, namely the variable within the array selected by the value of the index expression.
This
resulting variable, which is a component of the array, is never
considered final
, even if the array reference expression denoted a
final
variable.
An array access expression is evaluated using the following procedure:
First, the array reference expression is evaluated. If this evaluation completes abruptly, then the array access completes abruptly for the same reason and the index expression is not evaluated.
Otherwise, the index expression is evaluated. If this evaluation completes abruptly, then the array access completes abruptly for the same reason.
Otherwise, if the value of the array
reference expression is null
, then a NullPointerException
is thrown.
Otherwise, the value of the array
reference expression indeed refers to an array. If the value of
the index expression is less than zero, or greater than or equal
to the array's length
, then an ArrayIndexOutOfBoundsException
is
thrown.
Otherwise, the result of the array access is the variable of type T, within the array, selected by the value of the index expression.
Example 15.13-1. Array Reference Is Evaluated First
In an array access, the expression to the left of
the brackets appears to be fully evaluated before any part of the
expression within the brackets is evaluated. For example, in the
(admittedly monstrous) expression a[(a=b)[3]]
, the
expression a
is fully evaluated before the
expression (a=b)[3]
; this means that the original
value of a
is fetched and remembered while the
expression (a=b)[3]
is evaluated. This array
referenced by the original value of a
is then
subscripted by a value that is element 3
of another
array (possibly the same array) that was referenced
by b
and is now also referenced
by a
.
Thus, the program:
class Test1 { public static void main(String[] args) { int[] a = { 11, 12, 13, 14 }; int[] b = { 0, 1, 2, 3 }; System.out.println(a[(a=b)[3]]); } }
prints:
14
because the monstrous expression's value is
equivalent to a[b[3]]
or a[3]
or 14
.
Example 15.13-2. Abrupt Completion of Array Reference Evaluation
If evaluation of the expression to the left of the brackets completes abruptly, no part of the expression within the brackets will appear to have been evaluated. Thus, the program:
class Test2 { public static void main(String[] args) { int index = 1; try { skedaddle()[index=2]++; } catch (Exception e) { System.out.println(e + ", index=" + index); } } static int[] skedaddle() throws Exception { throw new Exception("Ciao"); } }
prints:
java.lang.Exception: Ciao, index=1
because the embedded assignment
of 2
to index
never
occurs.
Example 15.13-3. null
Array Reference
If the array reference expression produces null
instead of a reference to an array, then a NullPointerException
is thrown at run
time, but only after all parts of the array access expression have
been evaluated and only if these evaluations completed normally. Thus,
the program:
class Test3 { public static void main(String[] args) { int index = 1; try { nada()[index=2]++; } catch (Exception e) { System.out.println(e + ", index=" + index); } } static int[] nada() { return null; } }
prints:
java.lang.NullPointerException, index=2
because the embedded assignment
of 2
to index
occurs before the
check for a null
array reference expression. As a related example,
the program:
class Test4 { public static void main(String[] args) { int[] a = null; try { int i = a[vamoose()]; System.out.println(i); } catch (Exception e) { System.out.println(e); } } static int vamoose() throws Exception { throw new Exception("Twenty-three skidoo!"); } }
always prints:
java.lang.Exception: Twenty-three skidoo!
A NullPointerException
never occurs, because the index expression
must be completely evaluated before any part of the array access occurs, and that includes the check as to
whether the value of the array reference
expression is null
.
Postfix expressions include
uses of the postfix ++
and --
operators. Names are
not considered to be primary expressions (§15.8),
but are handled separately in the grammar to avoid certain
ambiguities. They become interchangeable only here, at the level of
precedence of postfix expressions.
PostfixExpression:
Primary
ExpressionName
PostIncrementExpression
PostDecrementExpression
The rules for evaluating expression names are given in §6.5.6.
A postfix expression
followed by a ++
operator is a postfix increment
expression.
PostIncrementExpression:
PostfixExpression ++
The result of the postfix expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.
The type of the postfix increment expression is the type of the variable. The result of the postfix increment expression is not a variable, but a value.
At run
time, if evaluation of the operand expression completes abruptly, then
the postfix increment expression completes abruptly for the same
reason and no incrementation occurs. Otherwise, the
value 1
is added to the value of the variable and
the sum is stored back into the variable. Before the addition, binary
numeric promotion (§5.6.2) is performed on the
value 1
and the value of the variable. If
necessary, the sum is narrowed by a narrowing primitive conversion
(§5.1.3) and/or subjected to boxing conversion
(§5.1.7) to the type of the variable before it is
stored. The value of the postfix increment expression is the value of
the variable before the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the sum prior to its being stored in the variable.
A variable
that is declared final
cannot be incremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
postfix increment operator.
A postfix expression
followed by a --
operator is a postfix decrement
expression.
PostDecrementExpression:
PostfixExpression --
The result of the postfix expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.
The type of the postfix decrement expression is the type of the variable. The result of the postfix decrement expression is not a variable, but a value.
At run
time, if evaluation of the operand expression completes abruptly, then
the postfix decrement expression completes abruptly for the same
reason and no decrementation occurs. Otherwise, the
value 1
is subtracted from the value of the
variable and the difference is stored back into the variable. Before
the subtraction, binary numeric promotion
(§5.6.2) is performed on the
value 1
and the value of the variable. If
necessary, the difference is narrowed by a narrowing primitive
conversion (§5.1.3) and/or subjected to boxing
conversion (§5.1.7) to the type of the variable
before it is stored. The value of the postfix decrement expression is
the value of the variable before the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the difference prior to its being stored in the variable.
A variable
that is declared final
cannot be decremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
postfix decrement operator.
The operators +
,
-
, ++
, --
, ~
, !
, and the cast
operator (§15.16) are called the unary
operators.
UnaryExpression:
PreIncrementExpression
PreDecrementExpression
+
UnaryExpression
-
UnaryExpression
UnaryExpressionNotPlusMinus
PreIncrementExpression:
++
UnaryExpression
PreDecrementExpression:
--
UnaryExpression
UnaryExpressionNotPlusMinus:
PostfixExpression
~
UnaryExpression
!
UnaryExpression
CastExpression
Expressions with unary operators group right-to-left, so
that -~x
means the same
as -(~x)
.
A unary expression preceded
by a ++
operator is a prefix increment expression.
The result of the unary expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.
The type of the prefix increment expression is the type of the variable. The result of the prefix increment expression is not a variable, but a value.
At run
time, if evaluation of the operand expression completes abruptly, then
the prefix increment expression completes abruptly for the same reason
and no incrementation occurs. Otherwise, the
value 1
is added to the value of the variable and
the sum is stored back into the variable. Before the addition, binary
numeric promotion (§5.6.2) is performed on the
value 1
and the value of the variable. If
necessary, the sum is narrowed by a narrowing primitive conversion
(§5.1.3) and/or subjected to boxing conversion
(§5.1.7) to the type of the variable before it is
stored. The value of the prefix increment expression is the value of
the variable after the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the sum prior to its being stored in the variable.
A variable
that is declared final
cannot be incremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
prefix increment operator.
A unary expression preceded
by a --
operator is a prefix decrement expression.
The result of the unary expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.
The type of the prefix decrement expression is the type of the variable. The result of the prefix decrement expression is not a variable, but a value.
At run time, if evaluation
of the operand expression completes abruptly, then the prefix
decrement expression completes abruptly for the same reason and no
decrementation occurs. Otherwise, the value 1
is
subtracted from the value of the variable and the difference is stored
back into the variable. Before the subtraction, binary numeric
promotion (§5.6.2) is performed on the
value 1
and the value of the variable. If
necessary, the difference is narrowed by a narrowing primitive
conversion (§5.1.3) and/or subjected to boxing
conversion (§5.1.7) to the type of the variable
before it is stored. The value of the prefix decrement expression is
the value of the variable after the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, format conversion is applied to the difference prior to its being stored in the variable.
A variable
that is declared final
cannot be decremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
prefix decrement operator.
The type
of the operand expression of the unary +
operator
must be a type that is convertible (§5.1.8) to a
primitive numeric type, or a compile-time error occurs.
Unary numeric promotion (§5.6.1) is performed on the operand. The type of the unary plus expression is the promoted type of the operand. The result of the unary plus expression is not a variable, but a value, even if the result of the operand expression is a variable.
At run time, the value of the unary plus expression is the promoted value of the operand.
The type
of the operand expression of the unary -
operator
must be a type that is convertible (§5.1.8) to a
primitive numeric type, or a compile-time error occurs.
Unary numeric promotion (§5.6.1) is performed on the operand.
The type of the unary minus expression is the promoted type of the operand.
Note that unary numeric promotion performs value set conversion (§5.1.13). Whatever value set the promoted operand value is drawn from, the unary negation operation is carried out and the result is drawn from that same value set. That result is then subject to further value set conversion.
At run time, the value of the unary minus expression is the arithmetic negation of the promoted value of the operand.
For
integer values, negation is the same as subtraction from zero. The
Java programming language uses two's-complement representation for integers, and the
range of two's-complement values is not symmetric, so negation of the
maximum negative int
or long
results in that same maximum negative
number. Overflow occurs in this case, but no exception is thrown. For
all integer values x
, -x
equals (~x)+1
.
For
floating-point values, negation is not the same
as subtraction from zero, because if x
is +0.0
, then 0.0-x
is +0.0
, but -x
is -0.0
. Unary minus merely inverts the sign of a
floating-point number. Special cases of interest:
If the operand is NaN, the result is NaN. (Recall that NaN has no sign (§4.2.3).)
If the operand is an infinity, the result is the infinity of opposite sign.
If the operand is a zero, the result is the zero of opposite sign.
The type
of the operand expression of the unary ~
operator
must be a type that is convertible (§5.1.8) to a
primitive integral type, or a compile-time error occurs.
Unary numeric promotion (§5.6.1) is performed on the operand. The type of the unary bitwise complement expression is the promoted type of the operand.
At run
time, the value of the unary bitwise complement expression is the
bitwise complement of the promoted value of the operand. In all
cases, ~x
equals (-x)-1
.
The type
of the operand expression of the unary !
operator
must be boolean
or Boolean
, or a compile-time error occurs.
The type
of the unary logical complement expression is boolean
.
At run
time, the operand is subject to unboxing conversion
(§5.1.8) if necessary. The value of the unary
logical complement expression is true
if the (possibly converted)
operand value is false
, and false
if the (possibly converted)
operand value is true
.
A cast
expression converts, at run time, a value of one numeric type to a
similar value of another numeric type; or confirms, at compile time,
that the type of an expression is boolean
; or checks, at run time,
that a reference value refers to an object whose class is compatible
with a specified reference type.
The parentheses and the type they contain are sometimes called the cast operator.
CastExpression:
(
PrimitiveType )
UnaryExpression
(
ReferenceType )
UnaryExpressionNotPlusMinus
See §15.15 for a discussion of the distinction between UnaryExpression and UnaryExpressionNotPlusMinus.
The type of a cast expression is the result of applying capture conversion (§5.1.10) to the type whose name appears within the parentheses.
The result of a cast expression is not a variable, but a value, even if the result of the operand expression is a variable.
A cast
operator has no effect on the choice of value set
(§4.2.3) for a value of type float
or type
double
. Consequently, a cast to type float
within an expression
that is not FP-strict (§15.4) does not
necessarily cause its value to be converted to an element of the float
value set, and a cast to type double
within an expression that is
not FP-strict does not necessarily cause its value to be converted to
an element of the double value set.
It is a compile-time error if the compile-time type of the operand may never be cast to the type specified by the cast operator according to the rules of casting conversion (§5.5).
Some casts result in an error at compile time. Some casts can be proven, at compile time, always to be correct at run time. For example, it is always correct to convert a value of a class type to the type of its superclass; such a cast should require no special action at run time. Finally, some casts cannot be proven to be either always correct or always incorrect at compile time. Such casts require a test at run time. See §5.5 for details.
Otherwise, at run time, the operand value is converted (if necessary) by casting conversion to the type specified by the cast operator.
A ClassCastException
is thrown if a cast is found at run time to be impermissible.
The operators *
,
/
, and %
are called the multiplicative
operators.
MultiplicativeExpression:
UnaryExpression
MultiplicativeExpression *
UnaryExpression
MultiplicativeExpression /
UnaryExpression
MultiplicativeExpression %
UnaryExpression
The multiplicative operators have the same precedence and are syntactically left-associative (they group left-to-right).
The type of each of the operands of a multiplicative operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
The type of a multiplicative expression is the promoted type of its operands.
If the
promoted type is int
or long
, then integer arithmetic is
performed.
If the
promoted type is float
or double
, then floating-point arithmetic
is performed.
The
binary *
operator performs multiplication,
producing the product of its operands.
Multiplication is a commutative operation if the operand expressions have no side effects.
Integer multiplication is associative when the operands are all of the same type.
Floating-point multiplication is not associative.
If an integer multiplication overflows, then the result is the low-order bits of the mathematical product as represented in some sufficiently large two's-complement format. As a result, if overflow occurs, then the sign of the result may not be the same as the sign of the mathematical product of the two operand values.
The result of a floating-point multiplication is determined by the rules of IEEE 754 arithmetic:
If the result is not NaN, the sign of the result is positive if both operands have the same sign, and negative if the operands have different signs.
Multiplication of an infinity by a finite value results in a signed infinity. The sign is determined by the rule stated above.
In the remaining cases, where neither an infinity nor NaN is involved, the exact mathematical product is computed. A floating-point value set is then chosen:
If the multiplication expression is FP-strict (§15.4):
If the multiplication expression is not FP-strict:
If the type of the
multiplication expression is float
, then either the
float value set or the float-extended-exponent value set
may be chosen, at the whim of the implementation.
If the type of the
multiplication expression is double
, then either the
double value set or the double-extended-exponent value set
may be chosen, at the whim of the implementation.
Next, a value must be chosen from the chosen value set to represent the product.
If the magnitude of the product is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign.
Otherwise, the product is rounded to the nearest value in the chosen value set using IEEE 754 round-to-nearest mode. The Java programming language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
Despite
the fact that overflow, underflow, or loss of information may occur,
evaluation of a multiplication operator *
never throws a
run-time exception.
The
binary /
operator performs division, producing the
quotient of its operands. The left-hand operand is
the dividend and the right-hand operand is
the divisor.
Integer
division rounds toward 0
. That is, the quotient
produced for operands n and d that are integers after binary
numeric promotion (§5.6.2) is an integer value
q whose magnitude is as large as possible while satisfying
|d · q| ≤ |n|. Moreover, q is positive
when |n| ≥ |d| and n and d have the same sign,
but q is negative when |n| ≥ |d| and n and
d have opposite signs.
There
is one special case that does not satisfy this rule: if the dividend
is the negative integer of largest possible magnitude for its type,
and the divisor is -1
, then integer overflow occurs
and the result is equal to the dividend. Despite the overflow, no
exception is thrown in this case. On the other hand, if the value of
the divisor in an integer division is 0
, then an
ArithmeticException
is thrown.
The result of a floating-point division is determined by the rules of IEEE 754 arithmetic:
If the result is not NaN, the sign of the result is positive if both operands have the same sign, and negative if the operands have different signs.
Division of an infinity by a finite value results in a signed infinity. The sign is determined by the rule stated above.
Division of a finite value by an infinity results in a signed zero. The sign is determined by the rule stated above.
Division of a zero by a zero results in NaN; division of zero by any other finite value results in a signed zero. The sign is determined by the rule stated above.
Division of a nonzero finite value by a zero results in a signed infinity. The sign is determined by the rule stated above.
In the remaining cases, where neither an infinity nor NaN is involved, the exact mathematical quotient is computed. A floating-point value set is then chosen:
If the division expression is FP-strict (§15.4):
If the division expression is not FP-strict:
If the type of the division
expression is float
, then either the float value set or
the float-extended-exponent value set may be chosen, at
the whim of the implementation.
If the type of the division
expression is double
, then either the double value set
or the double-extended-exponent value set may be chosen,
at the whim of the implementation.
Next, a value must be chosen from the chosen value set to represent the quotient.
If the magnitude of the quotient is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign.
Otherwise, the quotient is rounded to the nearest value in the chosen value set using IEEE 754 round-to-nearest mode. The Java programming language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
Despite
the fact that overflow, underflow, division by zero, or loss of
information may occur, evaluation of a floating-point division
operator /
never throws a run-time exception.
The
binary %
operator is said to yield the remainder of
its operands from an implied division; the left-hand operand is
the dividend and the right-hand operand is
the divisor.
In C and C++, the remainder operator accepts only integral operands, but in the Java programming language, it also accepts floating-point operands.
The
remainder operation for operands that are integers after binary
numeric promotion (§5.6.2) produces a result
value such that (a/b)*b+(a%b)
is equal
to a
.
This
identity holds even in the special case that the dividend is the
negative integer of largest possible magnitude for its type and the
divisor is -1
(the remainder
is 0
).
It follows from this rule that the result of the remainder operation can be negative only if the dividend is negative, and can be positive only if the dividend is positive. Moreover, the magnitude of the result is always less than the magnitude of the divisor.
If the
value of the divisor for an integer remainder operator
is 0
, then an ArithmeticException
is
thrown.
Example 15.17.3-1. Integer Remainder Operator
class Test1 { public static void main(String[] args) { int a = 5%3; // 2 int b = 5/3; // 1 System.out.println("5%3 produces " + a + " (note that 5/3 produces " + b + ")"); int c = 5%(-3); // 2 int d = 5/(-3); // -1 System.out.println("5%(-3) produces " + c + " (note that 5/(-3) produces " + d + ")"); int e = (-5)%3; // -2 int f = (-5)/3; // -1 System.out.println("(-5)%3 produces " + e + " (note that (-5)/3 produces " + f + ")"); int g = (-5)%(-3); // -2 int h = (-5)/(-3); // 1 System.out.println("(-5)%(-3) produces " + g + " (note that (-5)/(-3) produces " + h + ")"); } }
This program produces the output:
5%3 produces 2 (note that 5/3 produces 1) 5%(-3) produces 2 (note that 5/(-3) produces -1) (-5)%3 produces -2 (note that (-5)/3 produces -1) (-5)%(-3) produces -2 (note that (-5)/(-3) produces 1)
The result of a
floating-point remainder operation as computed by
the %
operator is not the same
as that produced by the remainder operation defined by IEEE 754. The
IEEE 754 remainder operation computes the remainder from a rounding
division, not a truncating division, and so its behavior
is not analogous to that of the usual integer
remainder operator. Instead, the Java programming language
defines %
on floating-point operations to behave in
a manner analogous to that of the integer remainder operator; this may
be compared with the C library function fmod
. The
IEEE 754 remainder operation may be computed by the library
routine Math.IEEEremainder
.
The result of a floating-point remainder operation is determined by the rules of IEEE 754 arithmetic:
If the result is not NaN, the sign of the result equals the sign of the dividend.
If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN.
If the dividend is finite and the divisor is an infinity, the result equals the dividend.
If the dividend is a zero and the divisor is finite, the result equals the dividend.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, the floating-point remainder r from the division of a dividend n by a divisor d is defined by the mathematical relation r = n - (d · q) where q is an integer that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d.
Evaluation of a floating-point remainder
operator %
never throws a run-time exception, even
if the right-hand operand is zero. Overflow, underflow, or loss of
precision cannot occur.
Example 15.17.3-2. Floating-Point Remainder Operator
class Test2 { public static void main(String[] args) { double a = 5.0%3.0; // 2.0 System.out.println("5.0%3.0 produces " + a); double b = 5.0%(-3.0); // 2.0 System.out.println("5.0%(-3.0) produces " + b); double c = (-5.0)%3.0; // -2.0 System.out.println("(-5.0)%3.0 produces " + c); double d = (-5.0)%(-3.0); // -2.0 System.out.println("(-5.0)%(-3.0) produces " + d); } }
This program produces the output:
5.0%3.0 produces 2.0 5.0%(-3.0) produces 2.0 (-5.0)%3.0 produces -2.0 (-5.0)%(-3.0) produces -2.0
The operators +
and
-
are called the
additive operators.
AdditiveExpression:
MultiplicativeExpression
AdditiveExpression +
MultiplicativeExpression
AdditiveExpression -
MultiplicativeExpression
The additive operators have the same precedence and are syntactically left-associative (they group left-to-right).
If the
type of either operand of a +
operator is String
,
then the operation is string concatenation.
Otherwise,
the type of each of the operands of the +
operator
must be a type that is convertible (§5.1.8) to a
primitive numeric type, or a compile-time error occurs.
In every
case, the type of each of the operands of the
binary -
operator must be a type that is
convertible (§5.1.8) to a primitive numeric type,
or a compile-time error occurs.
If only
one operand expression is of type String
, then string conversion
(§5.1.11) is performed on the other operand to
produce a string at run time.
The
result of string concatenation is a reference to a
String
object that is the concatenation of the two operand
strings. The characters of the left-hand operand precede the
characters of the right-hand operand in the newly created
string.
The
String
object is newly created (§12.5) unless
the expression is a compile-time constant expression
(§15.28).
An implementation may choose to perform conversion
and concatenation in one step to avoid creating and then discarding an
intermediate String
object. To increase the performance of repeated
string concatenation, a Java compiler may use
the StringBuffer
class or a similar technique to
reduce the number of intermediate String
objects that are created by
evaluation of an expression.
For primitive types, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.
Example 15.18.1-1. String Concatenation
The example expression:
"The square root of 2 is " + Math.sqrt(2)
produces the result:
"The square root of 2 is 1.4142135623730952"
The +
operator is syntactically
left-associative, no matter whether it is determined by type analysis
to represent string concatenation or numeric addition. In some cases
care is required to get the desired result. For example, the
expression:
a + b + c
is always regarded as meaning:
(a + b) + c
Therefore the result of the expression:
1 + 2 + " fiddlers"
is:
"3 fiddlers"
but the result of:
"fiddlers " + 1 + 2
is:
"fiddlers 12"
Example 15.18.1-2. String Concatenation and Conditionals
In this jocular little example:
class Bottles { static void printSong(Object stuff, int n) { String plural = (n == 1) ? "" : "s"; loop: while (true) { System.out.println(n + " bottle" + plural + " of " + stuff + " on the wall,"); System.out.println(n + " bottle" + plural + " of " + stuff + ";"); System.out.println("You take one down " + "and pass it around:"); --n; plural = (n == 1) ? "" : "s"; if (n == 0) break loop; System.out.println(n + " bottle" + plural + " of " + stuff + " on the wall!"); System.out.println(); } System.out.println("No bottles of " + stuff + " on the wall!"); } public static void main(String[] args) { printSong("slime", 3); } }
the method printSong
will print a
version of a children's song. Popular values
for stuff
include "pop
" and
"beer
"; the most popular value
for n
is 100
. Here is the output
that results from running the program:
3 bottles of slime on the wall, 3 bottles of slime; You take one down and pass it around: 2 bottles of slime on the wall! 2 bottles of slime on the wall, 2 bottles of slime; You take one down and pass it around: 1 bottle of slime on the wall! 1 bottle of slime on the wall, 1 bottle of slime; You take one down and pass it around: No bottles of slime on the wall!
In the code, note the careful conditional generation
of the singular "bottle
" when appropriate rather
than the plural "bottles
"; note also how the string
concatenation operator was used to break the long constant
string:
"You take one down and pass it around:"
into two pieces to avoid an inconveniently long line in the source code.
The
binary +
operator performs addition when applied to
two operands of numeric type, producing the sum of the
operands.
The
binary -
operator performs subtraction, producing
the difference of two numeric operands.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
The type of an additive expression on numeric operands is the promoted type of its operands.
If this
promoted type is int
or long
, then integer arithmetic is
performed.
If this
promoted type is float
or double
, then floating-point arithmetic
is performed.
Addition is a commutative operation if the operand expressions have no side effects.
Integer addition is associative when the operands are all of the same type.
Floating-point addition is not associative.
If an integer addition overflows, then the result is the low-order bits of the mathematical sum as represented in some sufficiently large two's-complement format. If overflow occurs, then the sign of the result is not the same as the sign of the mathematical sum of the two operand values.
The result of a floating-point addition is determined using the following rules of IEEE 754 arithmetic:
The sum of two infinities of the same sign is the infinity of that sign.
The sum of an infinity and a finite value is equal to the infinite operand.
The sum of two zeros of the same sign is the zero of that sign.
The sum of a zero and a nonzero finite value is equal to the nonzero operand.
The sum of two nonzero finite values of the same magnitude and opposite sign is positive zero.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, and the operands have the same sign or have different magnitudes, the exact mathematical sum is computed. A floating-point value set is then chosen:
If the addition expression is FP-strict (§15.4):
If the addition expression is not FP-strict:
If the type of the addition
expression is float
, then either the float value set or
the float-extended-exponent value set may be chosen, at
the whim of the implementation.
If the type of the addition
expression is double
, then either the double value set
or the double-extended-exponent value set may be chosen,
at the whim of the implementation.
Next, a value must be chosen from the chosen value set to represent the sum.
If the magnitude of the sum is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign.
Otherwise, the sum is rounded to the nearest value in the chosen value set using IEEE 754 round-to-nearest mode. The Java programming language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
The
binary -
operator performs subtraction when applied
to two operands of numeric type, producing the difference of its
operands; the left-hand operand is the minuend
and the right-hand operand is
the subtrahend.
For
both integer and floating-point subtraction, it is always the case
that a-b
produces the same result
as a+(-b)
.
Note
that, for integer values, subtraction from zero is the same as
negation. However, for floating-point operands, subtraction from zero
is not the same as negation, because if x
is +0.0
, then 0.0-x
is +0.0
, but -x
is -0.0
.
Despite the fact that overflow, underflow, or loss of information may occur, evaluation of a numeric additive operator never throws a run-time exception.
The operators <<
(left
shift), >>
(signed right shift), and >>>
(unsigned right
shift) are called the shift operators. The
left-hand operand of a shift operator is the value to be shifted; the
right-hand operand specifies the shift distance.
ShiftExpression:
AdditiveExpression
ShiftExpression <<
AdditiveExpression
ShiftExpression >>
AdditiveExpression
ShiftExpression >>>
AdditiveExpression
The shift operators are syntactically left-associative (they group left-to-right).
Unary numeric promotion (§5.6.1) is performed on each operand separately. (Binary numeric promotion (§5.6.2) is not performed on the operands.)
It is a compile-time error if the type of each of the operands of a shift operator, after unary numeric promotion, is not a primitive integral type.
The type of the shift expression is the promoted type of the left-hand operand.
If the
promoted type of the left-hand operand is int
, only the five
lowest-order bits of the right-hand operand are used as the shift
distance. It is as if the right-hand operand were subjected to a
bitwise logical AND operator &
(§15.22.1) with the mask
value 0x1f
(0b11111
). The shift
distance actually used is therefore always in the
range 0
to 31
, inclusive.
If the
promoted type of the left-hand operand is long
, then only the six
lowest-order bits of the right-hand operand are used as the shift
distance. It is as if the right-hand operand were subjected to a
bitwise logical AND operator &
(§15.22.1) with the mask
value 0x3f
(0b111111
). The shift
distance actually used is therefore always in the
range 0
to 63
, inclusive.
At run time, shift operations are performed on the two's-complement integer representation of the value of the left operand.
The value
of n <<
s is n left-shifted s bit positions;
this is equivalent (even if overflow occurs) to multiplication by two
to the power s.
The value
of n >>
s is n right-shifted s bit positions
with sign-extension. The resulting value is ⌊ n /
2
s ⌋. For
non-negative values of n, this is equivalent to truncating
integer division, as computed by the integer division
operator /
, by two to the power s.
The value
of n >>>
s is n right-shifted s bit
positions with zero-extension, where:
If
n is positive, then the result is the same as that of n
>>
s.
If
n is negative and the type of the left-hand operand is int
,
then the result is equal to that of the expression
(
n >>
s)
+
(
2
<<
~
s)
.
If
n is negative and the type of the left-hand operand is
long
, then the result is equal to that of the
expression
(
n >>
s)
+
(
2L
<<
~
s)
.
The added term (
2
<<
~
s)
or
(
2L
<<
~
s)
cancels out the propagated
sign bit.
Note that, because of the implicit masking of the
right-hand operand of a shift operator, ~
s as
a shift distance is equivalent to 31-
s when
shifting an int
value and to 63-
s when
shifting a long
value.
The numerical comparison
operators <
, >
, <=
, and
>=
, and the instanceof
operator, are called
the relational operators.
RelationalExpression:
ShiftExpression
RelationalExpression <
ShiftExpression
RelationalExpression >
ShiftExpression
RelationalExpression <=
ShiftExpression
RelationalExpression >=
ShiftExpression
RelationalExpression instanceof
ReferenceType
The relational operators are syntactically left-associative (they group left-to-right).
However, this fact is not useful. For
example, a<b<c
parses
as (a<b)<c
, which is always a compile-time
error, because the type of a<b
is always
boolean
and < is not an operator on boolean
values.
The type
of a relational expression is always boolean
.
The type of each of the operands of a numerical comparison operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
If the
promoted type of the operands is int
or long
, then signed integer
comparison is performed.
If the
promoted type is float
or double
, then floating-point comparison
is performed.
Comparison is carried out accurately on floating-point values, no matter what value sets their representing values were drawn from.
The result of a floating-point comparison, as determined by the specification of the IEEE 754 standard, is:
All values other than NaN are ordered, with negative infinity less than all finite values, and positive infinity greater than all finite values.
Positive zero and negative zero are considered equal.
For example, -0.0<0.0
is
false
, but -0.0<=0.0
is true
.
Note, however, that the
methods Math.min
and Math.max
treat negative zero as being
strictly smaller than positive zero.
Subject to these considerations for floating-point numbers, the following rules then hold for integer operands or for floating-point operands other than NaN:
The value produced by the <
operator is true
if the value of the left-hand operand is less
than the value of the right-hand operand, and otherwise is
false
.
The value produced by the
<=
operator is true
if the value of the
left-hand operand is less than or equal to the value of the
right-hand operand, and otherwise is false
.
The value produced by the
>
operator is true
if the value of the left-hand
operand is greater than the value of the right-hand operand, and
otherwise is false
.
The value produced by the
>=
operator is true
if the value of the
left-hand operand is greater than or equal to the value of the
right-hand operand, and otherwise is false
.
The type
of the RelationalExpression operand of the
instanceof
operator must be a reference type or the null type;
otherwise, a compile-time error occurs.
It is a
compile-time error if the ReferenceType mentioned
after the instanceof
operator does not denote a reference type that
is reifiable (§4.7).
If a cast
of the RelationalExpression to
the ReferenceType would be rejected as a
compile-time error, then the instanceof
relational expression
likewise produces a compile-time error. In such a situation, the
result of the instanceof
expression could never be true.
At run
time, the result of the instanceof
operator is true
if the value
of the RelationalExpression is not null
and the
reference could be cast (§15.16) to
the ReferenceType without raising a
ClassCastException
. Otherwise the result is false
.
Example 15.20.2-1. The instanceof
Operator
class Point { int x, y; } class Element { int atomicNumber; } class Test { public static void main(String[] args) { Point p = new Point(); Element e = new Element(); if (e instanceof Point) { // compile-time error System.out.println("I get your point!"); p = (Point)e; // compile-time error } } }
This program results in two compile-time errors. The
cast (Point)e
is incorrect because no instance
of Element
or any of its possible subclasses (none
are shown here) could possibly be an instance of any subclass
of Point
. The instanceof
expression is incorrect
for exactly the same reason. If, on the other hand, the
class Point
were a subclass
of Element
(an admittedly strange notion in this
example):
class Point extends Element { int x, y; }
then the cast would be possible, though it would
require a run-time check, and the instanceof
expression would then
be sensible and valid. The cast (Point)e
would
never raise an exception because it would not be executed if the value
of e
could not correctly be cast to
type Point
.
The
operators ==
(equal to) and !=
(not equal to) are called the equality
operators.
EqualityExpression:
RelationalExpression
EqualityExpression ==
RelationalExpression
EqualityExpression !=
RelationalExpression
The equality operators are syntactically left-associative (they group left-to-right).
However, this fact is essentially never useful. For
example, a==b==c
parses
as (a==b)==c
. The result type
of a==b
is always boolean
,
and c
must therefore be of type boolean
or a
compile-time error occurs. Thus, a==b==c
does not
test to see whether a
, b
,
and c
are all equal.
The equality operators are commutative if the operand expressions have no side effects.
The
equality operators are analogous to the relational operators except
for their lower precedence. Thus, a<b==c<d
is
true
whenever a<b
and
c<d
have the same truth value.
The
equality operators may be used to compare two operands that are
convertible (§5.1.8) to numeric type, or two
operands of type boolean
or Boolean
, or two operands that are each
of either reference type or the null type. All other cases result in a
compile-time error.
The type
of an equality expression is always boolean
.
In all
cases, a!=b
produces the same result
as !(a==b)
.
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
If the
promoted type of the operands is int
or long
, then an integer
equality test is performed.
If the
promoted type is float
or double
, then a floating-point equality
test is performed.
Comparison is carried out accurately on floating-point values, no matter what value sets their representing values were drawn from.
Floating-point equality testing is performed in accordance with the rules of the IEEE 754 standard:
If either operand is NaN, then the
result of ==
is false
but the result
of !=
is true
.
Indeed, the
test x!=x
is true
if and only if the value
of x
is NaN.
The methods Float.isNaN
and Double.isNaN
may also be used to test
whether a value is NaN.
Positive zero and negative zero are considered equal.
For example, -0.0==0.0
is
true
.
Otherwise, two distinct floating-point values are considered unequal by the equality operators.
In particular, there is one value representing positive infinity and one value representing negative infinity; each compares equal only to itself, and each compares unequal to all other values.
Subject to these considerations for floating-point numbers, the following rules then hold for integer operands or for floating-point operands other than NaN:
The value produced by
the ==
operator is true
if the value of the
left-hand operand is equal to the value of the right-hand operand;
otherwise, the result is false
.
The value produced by
the !=
operator is true
if the value of the
left-hand operand is not equal to the value of the right-hand
operand; otherwise, the result is false
.
If the
operands of an equality operator are both of type boolean
, or if one
operand is of type boolean
and the other is of type Boolean
, then
the operation is boolean equality.
The boolean equality operators are associative.
If one
of the operands is of type Boolean
, it is subjected to unboxing
conversion (§5.1.8).
The
result of ==
is true
if the operands (after any
required unboxing conversion) are both true
or both false
;
otherwise, the result is false
.
The
result of !=
is false
if the operands are both
true
or both false
; otherwise, the result is true
.
Thus !=
behaves the same
as ^
(§15.22.2) when applied
to boolean
operands.
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion (§5.5). The run-time values of the two operands would necessarily be unequal.
At
run time, the result of ==
is true
if the operand
values are both null
or both refer to the same object or array;
otherwise, the result is false
.
The
result of !=
is false
if the operand values are both
null
or both refer to the same object or array; otherwise, the
result is true
.
While ==
may be used to compare references of type String
, such an equality
test determines whether or not the two operands refer to the same
String
object. The result is false
if the operands are distinct
String
objects, even if they contain the same sequence of characters
(§3.10.5). The contents of two
strings s
and t
can be tested
for equality by the method
invocation s.equals(t)
.
The bitwise
operators and logical operators
include the AND operator &
, exclusive OR
operator ^
, and inclusive OR
operator |
.
AndExpression:
EqualityExpression
AndExpression &
EqualityExpression
ExclusiveOrExpression:
AndExpression
ExclusiveOrExpression ^
AndExpression
InclusiveOrExpression:
ExclusiveOrExpression
InclusiveOrExpression |
ExclusiveOrExpression
These
operators have different precedence, with &
having the highest precedence and |
the lowest
precedence.
Each of these operators is syntactically left-associative (each groups left-to-right).
Each operator is commutative if the operand expressions have no side effects.
The bitwise
and logical operators may be used to compare two operands of numeric
type or two operands of type boolean
. All other cases result in a
compile-time error.
When
both operands of an operator &
,
^
, or |
are of a type that is
convertible (§5.1.8) to a primitive integral
type, binary numeric promotion is first performed on the operands
(§5.6.2).
The type of the bitwise operator expression is the promoted type of the operands.
For &
, the result
value is the bitwise AND of the operand values.
For ^
, the result value is
the bitwise exclusive OR of the operand values.
For |
, the result value is
the bitwise inclusive OR of the operand values.
For example, the result of the expression:
0xff00 & 0xf0f0
is:
0xf000
The result of the expression:
0xff00 ^ 0xf0f0
is:
0x0ff0
The result of the expression:
0xff00 | 0xf0f0
is:
0xfff0
When
both operands of a &
, ^
,
or |
operator are of type boolean
or Boolean
,
then the type of the bitwise operator expression is boolean
. In all
cases, the operands are subject to unboxing conversion
(§5.1.8) as necessary.
For &
, the result
value is true
if both operand values are true
; otherwise, the
result is false
.
For ^
, the result value is
true
if the operand values are different; otherwise, the result is
false
.
For |
, the result value is
false
if both operand values are false
; otherwise, the result is
true
.
The
conditional-and operator &&
is like &
(§15.22.2), but evaluates its right-hand operand
only if the value of its left-hand operand is true
.
ConditionalAndExpression:
InclusiveOrExpression
ConditionalAndExpression &&
InclusiveOrExpression
The conditional-and operator is syntactically left-associative (it groups left-to-right).
The
conditional-and operator is fully associative with respect to both
side effects and result value. That is, for any
expressions a
, b
,
and c
, evaluation of the
expression ((
produces the same result, with the same side effects occurring in the
same order, as evaluation of the
expression a
) &&
(b
)) &&
(c
)(
.
a
) &&
((b
) &&
(c
))
Each
operand of the conditional-and operator must be of type boolean
or
Boolean
, or a compile-time error occurs.
The type
of a conditional-and expression is always boolean
.
At run
time, the left-hand operand expression is evaluated first; if the
result has type Boolean
, it is subjected to unboxing conversion
(§5.1.8).
If the
resulting value is false
, the value of the conditional-and
expression is false
and the right-hand operand expression is not
evaluated.
If the
value of the left-hand operand is true
, then the right-hand
expression is evaluated; if the result has type Boolean
, it is
subjected to unboxing conversion (§5.1.8). The
resulting value becomes the value of the conditional-and
expression.
Thus,
&&
computes the same result as &
on
boolean
operands. It differs only in that the right-hand operand
expression is evaluated conditionally rather than always.
The
conditional-or operator ||
operator is like |
(§15.22.2), but evaluates its right-hand operand
only if the value of its left-hand operand is false
.
ConditionalOrExpression:
ConditionalAndExpression
ConditionalOrExpression ||
ConditionalAndExpression
The conditional-or operator is syntactically left-associative (it groups left-to-right).
The
conditional-or operator is fully associative with respect to both side
effects and result value. That is, for any
expressions a
, b
,
and c
, evaluation of the
expression ((
produces the same result, with the same side effects occurring in the
same order, as evaluation of the
expression a
) ||
(b
)) ||
(c
)(
.
a
) ||
((b
) ||
(c
))
Each
operand of the conditional-or operator must be of type boolean
or
Boolean
, or a compile-time error occurs.
The type
of a conditional-or expression is always boolean
.
At run
time, the left-hand operand expression is evaluated first; if the
result has type Boolean
, it is subjected to unboxing conversion
(§5.1.8).
If the
resulting value is true
, the value of the conditional-or expression
is true
and the right-hand operand expression is not
evaluated.
If the
value of the left-hand operand is false
, then the right-hand
expression is evaluated; if the result has type Boolean
, it is
subjected to unboxing conversion (§5.1.8). The
resulting value becomes the value of the conditional-or
expression.
Thus,
||
compures the same result as |
on boolean
or Boolean
operands. It differs only in that the right-hand operand
expression is evaluated conditionally rather than always.
The conditional operator
? :
uses the boolean value of one expression to decide which of
two other expressions should be evaluated.
ConditionalExpression:
ConditionalOrExpression
ConditionalOrExpression ?
Expression :
ConditionalExpression
The
conditional operator is syntactically right-associative (it groups
right-to-left). Thus, a?b:c?d:e?f:g
means the same
as a?b:(c?d:(e?f:g))
.
The conditional operator has
three operand expressions. ?
appears between the first and second
expressions, and :
appears between the second and
third expressions.
The first
expression must be of type boolean
or Boolean
, or a compile-time
error occurs.
It is a
compile-time error for either the second or the third operand
expression to be an invocation of a void
method.
In fact, by the grammar of expression statements
(§14.8), it is not permitted for a conditional
expression to appear in any context where an invocation of a void
method could appear.
The type of a conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type
byte
or Byte
and the other is of type short
or Short
,
then the type of the conditional expression is short
.
If one of the operands is of type
T where T is byte
, short
, or char
, and the other
operand is a constant expression (§15.28)
of type int
whose value is representable in type T, then
the type of the conditional expression is T.
If one of the operands is of type T, where T
is Byte
, Short
, or Character
, and the other operand is a
constant expression (§15.28) of type
int
whose value is representable in the type U which is
the result of applying unboxing conversion to T, then the
type of the conditional expression is U.
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2.
The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
At run time, the first operand expression of the conditional expression is evaluated first. If necessary, unboxing conversion is performed on the result.
The
resulting boolean
value is then used to choose either the second or
the third operand expression:
The chosen operand expression is then evaluated and the resulting value is converted to the type of the conditional expression as determined by the rules stated above.
This conversion may include boxing (§5.1.7) or unboxing (§5.1.8) conversion.
The operand expression not chosen is not evaluated for that particular evaluation of the conditional expression.
There are
12 assignment operators; all are syntactically
right-associative (they group
right-to-left). Thus, a=b=c
means a=(b=c)
, which assigns the value
of c
to b
and then assigns the
value of b
to a
.
AssignmentExpression:
ConditionalExpression
Assignment
Assignment:
LeftHandSide AssignmentOperator AssignmentExpression
LeftHandSide:
ExpressionName
FieldAccess
ArrayAccess
AssignmentOperator: one of
= *= /= %= += -= <<= >>= >>>= &= ^= |=
The result of the first operand of an assignment operator must be a variable, or a compile-time error occurs.
This operand may be a named variable, such as a local variable or a field of the current object or class, or it may be a computed variable, as can result from a field access (§15.11) or an array access (§15.13).
The type of the assignment expression is the type of the variable after capture conversion (§5.1.10).
At run time, the result of the assignment expression is the value of the variable after the assignment has occurred. The result of an assignment expression is not itself a variable.
A variable
that is declared final
cannot be assigned to (unless it is
definitely unassigned (§16)), because when an
access of such a final
variable is used as an expression, the result
is a value, not a variable, and so it cannot be used as the first
operand of an assignment operator.
A compile-time error occurs if the type of the right-hand operand cannot be converted to the type of the variable by assignment conversion (§5.2).
At run time, the expression is evaluated in one of three ways.
If the
left-hand operand expression is a field access expression
(§15.11) e.f
, possibly
enclosed in one or more pairs of parentheses, then:
First, the
expression e
is evaluated. If evaluation
of e
completes abruptly, the assignment
expression completes abruptly for the same reason.
Next, the right hand operand is evaluated. If evaluation of the right hand expression completes abruptly, the assignment expression completes abruptly for the same reason.
Then, if the field denoted
by e.f
is not static
and the result of the
evaluation of e
above is null
, then a NullPointerException
is thrown.
Otherwise, the variable denoted
by e.f
is assigned the value of the right
hand operand as computed above.
If the left-hand operand is an array access expression (§15.13), possibly enclosed in one or more pairs of parentheses, then:
First, the array reference subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the index subexpression (of the left-hand operand array access expression) and the right-hand operand are not evaluated and no assignment occurs.
Otherwise, the index subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, if the value of the array
reference subexpression is null
, then no assignment occurs and
a NullPointerException
is thrown.
Otherwise, the value of the array
reference subexpression indeed refers to an array. If the value
of the index subexpression is less than zero, or greater than or
equal to the length
of the array, then no
assignment occurs and an ArrayIndexOutOfBoundsException
is thrown.
Otherwise, the value of the index subexpression is used to select a component of the array referred to by the value of the array reference subexpression.
This component is a variable; call its type SC. Also, let TC be the type of the left-hand operand of the assignment operator as determined at compile time. Then there are two possibilities:
If TC is a primitive type, then SC is necessarily the same as TC.
The value of the right-hand operand is converted to the type of the selected array component, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the array component.
If TC is a reference type, then SC may not be the same as TC, but rather a type that extends or implements TC.
Let RC be the class of the object referred to by the value of the right-hand operand at run time.
A Java compiler may be able
to prove at compile time that the array component will be of
type TC exactly (for example, TC might be final
). But
if a Java compiler cannot prove at compile time that the
array component will be of type TC exactly, then a check
must be performed at run time to ensure that the class RC
is assignment compatible (§5.2) with
the actual type SC of the array component.
This check is similar to a narrowing cast
(§5.5, §15.16),
except that if the check fails, an ArrayStoreException
is thrown rather
than a ClassCastException
.
If class RC is not
assignable to type SC, then no assignment occurs and an
ArrayStoreException
is thrown.
Otherwise, the reference value of the right-hand operand is stored into the selected array component.
Otherwise, three steps are required:
First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the value of the right-hand operand is converted to the type of the left-hand variable, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.
Example 15.26.1-1. Simple Assignment To An Array Component
class ArrayReferenceThrow extends RuntimeException { } class IndexThrow extends RuntimeException { } class RightHandSideThrow extends RuntimeException { } class IllustrateSimpleArrayAssignment { static Object[] objects = { new Object(), new Object() }; static Thread[] threads = { new Thread(), new Thread() }; static Object[] arrayThrow() { throw new ArrayReferenceThrow(); } static int indexThrow() { throw new IndexThrow(); } static Thread rightThrow() { throw new RightHandSideThrow(); } static String name(Object q) { String sq = q.getClass().getName(); int k = sq.lastIndexOf('.'); return (k < 0) ? sq : sq.substring(k+1); } static void testFour(Object[] x, int j, Object y) { String sx = x == null ? "null" : name(x[0]) + "s"; String sy = name(y); System.out.println(); try { System.out.print(sx + "[throw]=throw => "); x[indexThrow()] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[throw]=" + sy + " => "); x[indexThrow()] = y; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]=throw => "); x[j] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]=" + sy + " => "); x[j] = y; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } } public static void main(String[] args) { try { System.out.print("throw[throw]=throw => "); arrayThrow()[indexThrow()] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]=Thread => "); arrayThrow()[indexThrow()] = new Thread(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]=throw => "); arrayThrow()[1] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]=Thread => "); arrayThrow()[1] = new Thread(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } testFour(null, 1, new StringBuffer()); testFour(null, 9, new Thread()); testFour(objects, 1, new StringBuffer()); testFour(objects, 1, new Thread()); testFour(objects, 9, new StringBuffer()); testFour(objects, 9, new Thread()); testFour(threads, 1, new StringBuffer()); testFour(threads, 1, new Thread()); testFour(threads, 9, new StringBuffer()); testFour(threads, 9, new Thread()); } }
This program produces the output:
throw[throw]=throw => ArrayReferenceThrow throw[throw]=Thread => ArrayReferenceThrow throw[1]=throw => ArrayReferenceThrow throw[1]=Thread => ArrayReferenceThrow null[throw]=throw => IndexThrow null[throw]=StringBuffer => IndexThrow null[1]=throw => RightHandSideThrow null[1]=StringBuffer => NullPointerException null[throw]=throw => IndexThrow null[throw]=Thread => IndexThrow null[9]=throw => RightHandSideThrow null[9]=Thread => NullPointerException Objects[throw]=throw => IndexThrow Objects[throw]=StringBuffer => IndexThrow Objects[1]=throw => RightHandSideThrow Objects[1]=StringBuffer => Okay! Objects[throw]=throw => IndexThrow Objects[throw]=Thread => IndexThrow Objects[1]=throw => RightHandSideThrow Objects[1]=Thread => Okay! Objects[throw]=throw => IndexThrow Objects[throw]=StringBuffer => IndexThrow Objects[9]=throw => RightHandSideThrow Objects[9]=StringBuffer => ArrayIndexOutOfBoundsException Objects[throw]=throw => IndexThrow Objects[throw]=Thread => IndexThrow Objects[9]=throw => RightHandSideThrow Objects[9]=Thread => ArrayIndexOutOfBoundsException Threads[throw]=throw => IndexThrow Threads[throw]=StringBuffer => IndexThrow Threads[1]=throw => RightHandSideThrow Threads[1]=StringBuffer => ArrayStoreException Threads[throw]=throw => IndexThrow Threads[throw]=Thread => IndexThrow Threads[1]=throw => RightHandSideThrow Threads[1]=Thread => Okay! Threads[throw]=throw => IndexThrow Threads[throw]=StringBuffer => IndexThrow Threads[9]=throw => RightHandSideThrow Threads[9]=StringBuffer => ArrayIndexOutOfBoundsException Threads[throw]=throw => IndexThrow Threads[throw]=Thread => IndexThrow Threads[9]=throw => RightHandSideThrow Threads[9]=Thread => ArrayIndexOutOfBoundsException
The most interesting case of the lot is thirteenth from the end:
Threads[1]=StringBuffer => ArrayStoreException
which indicates that the attempt to store a
reference to a StringBuffer
into an array whose
components are of type Thread
throws an ArrayStoreException
. The code is
type-correct at compile time: the assignment has a left-hand side of
type Object[]
and a right-hand side of type
Object
. At run time, the first actual argument to
method testFour
is a reference to an instance of
"array of Thread
" and the third actual argument is a reference to an
instance of class StringBuffer
.
A
compound assignment expression of the form E1 op=
E2
is equivalent to E1 = (T) ((E1) op
(E2))
, where T is the type of E1
,
except that E1
is evaluated only once.
For example, the following code is correct:
short x = 3; x += 4.6;
and results in x
having the
value 7
because it is equivalent to:
short x = 3; x = (short)(x + 4.6);
At run time, the expression is evaluated in one of two ways.
If the left-hand operand expression is not an array access expression, then:
First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the saved value of the left-hand variable and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator. If this operation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the result of the binary operation is converted to the type of the left-hand variable, subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.
If the left-hand operand expression is an array access expression (§15.13), then:
First, the array reference subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the index subexpression (of the left-hand operand array access expression) and the right-hand operand are not evaluated and no assignment occurs.
Otherwise, the index subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and the right-hand operand is not evaluated and no assignment occurs.
Otherwise, if the value of the array
reference subexpression is null
, then no assignment occurs and a
NullPointerException
is thrown.
Otherwise, the value of the array
reference subexpression indeed refers to an array. If the value of
the index subexpression is less than zero, or greater than or
equal to the length
of the array, then no assignment occurs and
an ArrayIndexOutOfBoundsException
is thrown.
Otherwise, the value of the index subexpression is used to select a component of the array referred to by the value of the array reference subexpression. The value of this component is saved and then the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
For a simple assignment operator, the evaluation of the right-hand operand occurs before the checks of the array reference subexpression and the index subexpression, but for a compound assignment operator, the evaluation of the right-hand operand occurs after these checks.
Otherwise, consider the array component selected in the previous step, whose value was saved. This component is a variable; call its type S. Also, let T be the type of the left-hand operand of the assignment operator as determined at compile time.
If T is a primitive type, then S is necessarily the same as T.
The saved value of the array component and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator.
If this operation completes abruptly (the only possibility is an integer division by zero - see §15.17.2), then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the result of the binary operation is converted to the type of the selected array component, subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the array component.
If T is a
reference type, then it must be String
. Because class
String
is a final
class, S must also be String
.
Therefore the run-time check that is sometimes required for the simple assignment operator is never required for a compound assignment operator.
The saved value of the array
component and the value of the right-hand operand are used to
perform the binary operation (string concatenation) indicated
by the compound assignment operator (which is
necessarily +=
). If this operation
completes abruptly, then the assignment expression completes
abruptly for the same reason and no assignment occurs.
Otherwise, the String
result
of the binary operation is stored into the array
component.
Example 15.26.2-1. Compound Assignment To An Array Component
class ArrayReferenceThrow extends RuntimeException { } class IndexThrow extends RuntimeException { } class RightHandSideThrow extends RuntimeException { } class IllustrateCompoundArrayAssignment { static String[] strings = { "Simon", "Garfunkel" }; static double[] doubles = { Math.E, Math.PI }; static String[] stringsThrow() { throw new ArrayReferenceThrow(); } static double[] doublesThrow() { throw new ArrayReferenceThrow(); } static int indexThrow() { throw new IndexThrow(); } static String stringThrow() { throw new RightHandSideThrow(); } static double doubleThrow() { throw new RightHandSideThrow(); } static String name(Object q) { String sq = q.getClass().getName(); int k = sq.lastIndexOf('.'); return (k < 0) ? sq : sq.substring(k+1); } static void testEight(String[] x, double[] z, int j) { String sx = (x == null) ? "null" : "Strings"; String sz = (z == null) ? "null" : "doubles"; System.out.println(); try { System.out.print(sx + "[throw]+=throw => "); x[indexThrow()] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[throw]+=throw => "); z[indexThrow()] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[throw]+=\"heh\" => "); x[indexThrow()] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[throw]+=12345 => "); z[indexThrow()] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]+=throw => "); x[j] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[" + j + "]+=throw => "); z[j] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]+=\"heh\" => "); x[j] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[" + j + "]+=12345 => "); z[j] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } } public static void main(String[] args) { try { System.out.print("throw[throw]+=throw => "); stringsThrow()[indexThrow()] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=throw => "); doublesThrow()[indexThrow()] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=\"heh\" => "); stringsThrow()[indexThrow()] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=12345 => "); doublesThrow()[indexThrow()] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=throw => "); stringsThrow()[1] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=throw => "); doublesThrow()[1] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=\"heh\" => "); stringsThrow()[1] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=12345 => "); doublesThrow()[1] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } testEight(null, null, 1); testEight(null, null, 9); testEight(strings, doubles, 1); testEight(strings, doubles, 9); } }
This program produces the output:
throw[throw]+=throw => ArrayReferenceThrow throw[throw]+=throw => ArrayReferenceThrow throw[throw]+="heh" => ArrayReferenceThrow throw[throw]+=12345 => ArrayReferenceThrow throw[1]+=throw => ArrayReferenceThrow throw[1]+=throw => ArrayReferenceThrow throw[1]+="heh" => ArrayReferenceThrow throw[1]+=12345 => ArrayReferenceThrow null[throw]+=throw => IndexThrow null[throw]+=throw => IndexThrow null[throw]+="heh" => IndexThrow null[throw]+=12345 => IndexThrow null[1]+=throw => NullPointerException null[1]+=throw => NullPointerException null[1]+="heh" => NullPointerException null[1]+=12345 => NullPointerException null[throw]+=throw => IndexThrow null[throw]+=throw => IndexThrow null[throw]+="heh" => IndexThrow null[throw]+=12345 => IndexThrow null[9]+=throw => NullPointerException null[9]+=throw => NullPointerException null[9]+="heh" => NullPointerException null[9]+=12345 => NullPointerException Strings[throw]+=throw => IndexThrow doubles[throw]+=throw => IndexThrow Strings[throw]+="heh" => IndexThrow doubles[throw]+=12345 => IndexThrow Strings[1]+=throw => RightHandSideThrow doubles[1]+=throw => RightHandSideThrow Strings[1]+="heh" => Okay! doubles[1]+=12345 => Okay! Strings[throw]+=throw => IndexThrow doubles[throw]+=throw => IndexThrow Strings[throw]+="heh" => IndexThrow doubles[throw]+=12345 => IndexThrow Strings[9]+=throw => ArrayIndexOutOfBoundsException doubles[9]+=throw => ArrayIndexOutOfBoundsException Strings[9]+="heh" => ArrayIndexOutOfBoundsException doubles[9]+=12345 => ArrayIndexOutOfBoundsException
The most interesting cases of the lot are eleventh and twelfth from the end:
Strings[1]+=throw => RightHandSideThrow doubles[1]+=throw => RightHandSideThrow
They are the cases where a right-hand side that throws an exception actually gets to throw the exception; moreover, they are the only such cases in the lot. This demonstrates that the evaluation of the right-hand operand indeed occurs after the checks for a null array reference value and an out-of-bounds index value.
Example 15.26.2-2. Value Of Left-Hand Side Of Compound Assignment Is Saved Before Evaluation Of Right-Hand Side
class Test { public static void main(String[] args) { int k = 1; int[] a = { 1 }; k += (k = 4) * (k + 2); a[0] += (a[0] = 4) * (a[0] + 2); System.out.println("k==" + k + " and a[0]==" + a[0]); } }
This program produces the output:
k==25 and a[0]==25
The value 1
of k
is saved by the compound assignment
operator +=
before its right-hand
operand (k = 4) * (k + 2)
is evaluated. Evaluation
of this right-hand operand then assigns 4
to k
, calculates the value 6
for k + 2
, and then multiplies 4
by 6
to get 24
. This is added to
the saved value 1
to get 25
,
which is then stored into k
by
the +=
operator. An identical analysis applies to
the case that uses a[0]
.
In short, the statements:
k += (k = 4) * (k + 2); a[0] += (a[0] = 4) * (a[0] + 2);
behave in exactly the same manner as the statements:
k = k + (k = 4) * (k + 2); a[0] = a[0] + (a[0] = 4) * (a[0] + 2);
An Expression is any assignment expression:
Expression:
AssignmentExpression
Unlike C and C++, the Java programming language has no comma operator.
ConstantExpression:
Expression
A
compile-time constant expression is an expression
denoting a value of primitive type or a String
that does not
complete abruptly and is composed using only the following:
Literals of primitive type and literals
of type String
(§3.10.1,
§3.10.2, §3.10.3,
§3.10.4, §3.10.5)
Casts to primitive types and casts to
type String
(§15.16)
The
unary operators +
, -
, ~
, and !
(but not
++
or --
) (§15.15.3,
§15.15.4, §15.15.5,
§15.15.6)
The
multiplicative operators *
, /
, and %
(§15.17)
The
additive operators +
and -
(§15.18)
The
shift operators <<
, >>
, and >>>
(§15.19)
The
relational operators <
, <=
,
>
, and >=
(but not instanceof
)
(§15.20)
The
equality operators ==
and !=
(§15.21)
The
bitwise and logical operators &
, ^
, and |
(§15.22)
The
conditional-and operator &&
and the conditional-or operator
||
(§15.23,
§15.24)
The
ternary conditional operator ? :
(§15.25)
Parenthesized expressions (§15.8.5) whose contained expression is a constant expression.
Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).
Qualified names
(§6.5.6.2) of the
form TypeName .
Identifier that refer
to constant variables (§4.12.4).
Compile-time constant expressions of type
String
are always "interned" so as to share unique instances, using
the method String.intern
.
A compile-time constant expression is always treated as FP-strict (§15.4), even if it occurs in a context where a non-constant expression would not be considered to be FP-strict.
Compile-time constant expressions are used in case
labels in switch
statements (§14.11) and have a
special significance for assignment conversion
(§5.2) and initialization of a class or interface
(§12.4.2). They may also govern the ability of a
while
, do
, or for
statement to complete normally
(§14.21), and the type of a conditional operator
? :
with numeric operands.
Example 15.28-1. Constant Expressions
true (short)(1*2*3*4*5*6) Integer.MAX_VALUE / 2 2.0 * Math.PI "The integer " + Long.MAX_VALUE + " is mighty big."