Table of Contents
&&
||
? :
switch
ExpressionsMuch 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:
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.
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) or as the
single expression of a lambda body (§15.27.2),
because every other context in which an expression can appear requires
the expression to denote something. An expression statement
or lambda body 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.
Evaluation of an expression can produce side effects, because
expressions may contain embedded assignments, increment operators,
decrement operators, method invocations, and, in switch
expressions, arbitrary statements.
An expression occurs in either:
The declaration of some class or interface that is being declared: in a field initializer, in a static initializer, in an instance initializer, in a constructor declaration, in a method declaration, or in an annotation.
An annotation on the declaration of a module, a package, or a top level class or interface.
Expressions can be broadly categorized into one of the following syntactic forms:
Precedence among operators is managed by a hierarchy of grammar
productions. The lowest precedence operator is the arrow of a lambda
expression (->
), followed by the assignment operators. Thus, all
expressions are syntactically included in the
LambdaExpression and
AssignmentExpression nonterminals:
When some expressions appear in certain contexts, they are considered poly expressions. The following forms of expressions may be poly expressions:
The rules determining whether an expression of one of these forms is a poly expression are given in the individual sections that specify these forms of expressions.
Expressions that are not poly expressions are standalone expressions. Standalone expressions are expressions of the forms above when determined not to be poly expressions, as well as all expressions of all other forms. Expressions of all other forms are said to have a standalone form.
Some expressions have a value that can be determined at compile time. These are constant expressions (§15.29).
If an expression denotes a variable or a value, then the expression has a type known at compile time. The type of a standalone expression can be determined entirely from the contents of the expression; in contrast, the type of a poly expression may be influenced by the expression's target type (§5 (Conversions and Contexts)). 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 occurs (§4.12.2).
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 if the type of an expression is a class type which names the
class C, then the declaration of class C as final
or sealed
(§8.1.1.2) has implications for the value of the
expression:
If C is final
, then the expression is guaranteed to have a
value that is either (i) the null reference, or (ii) an object whose
class is C itself, because final
classes have no subclasses.
If C is sealed
, then the expression is guaranteed to have a
value that is either (i) the null reference, (ii) an object whose
class is C itself, or (iii) assignment compatible with one of the
permitted direct subclasses of C (§8.1.6).
If C is freely extensible, then the expression is guaranteed to have a value that is either (i) the null reference, (ii) an object whose class is C itself, or (iii) assignment compatible with C.
A floating-point expression is an expression whose type is float
or
double
(§4.2.3). Floating-point expressions of
type float
denote values that exactly correspond to the values
representable in the 32-bit IEEE 754 binary32 format. Floating-point
expressions of type double
denote values that exactly correspond to
the values representable in the 64-bit IEEE 754 binary64 format.
Many of the comparison and numerical operators of the Java programming language that can be used to form floating-point expressions correspond to IEEE 754 operations, as do the conversions that act on floating-point values (Table 15.4-A).
Table 15.4-A. Correspondence with IEEE 754 operations
Operator/Conversion | IEEE 754 operation |
---|---|
The numerical comparison operators < ,
<= , > , and >=
(§15.20.1)
|
compareQuietLess, compareQuietLessEqual, compareQuietGreater, compareQuietGreaterEqual |
The numerical equality operators == and !=
(§15.21.1)
|
compareQuietEqual, compareQuietNotEqual |
The unary minus operator -
(§15.15.4)
|
negate |
The multiplicative operators * and /
(§15.17.1, §15.17.2)
|
multiplication, division |
The additive operators + and -
(§15.18.2)
|
addition, subtraction |
Widening primitive conversion from an integral type (§5.1.2) | convertFromInt |
Narrowing primitive conversion to an integral type (§5.1.3) | convertToIntegerTowardZero |
Conversion between float and double |
convertFormat |
The floating-point remainder operator %
(§15.17.3) does not correspond to the IEEE 754
remainder operation.
Some IEEE 754 operations without corresponding
operators in the Java programming language are provided via methods in the
Math
and StrictMath
classes,
including the sqrt
method for the IEEE 754
squareRoot operation, the fma
method for the IEEE
754 fusedMultiplyAdd operation, and the IEEEremainder
method for the IEEE 754 remainder operation.
The Java programming language requires support of IEEE 754 subnormal floating-point numbers and gradual underflow, which make it easier to prove desirable properties of particular numerical algorithms. Floating-point operations do not "flush to zero" if the calculated result is a subnormal number.
The result of a floating-point operator of the Java programming language must match the result of the corresponding IEEE 754 operation on the same operands. For finite results, this implies the sign, significand, and exponent of the floating-point result must all be those specified by IEEE 754.
The requirement for matching sign, significand, and
exponent precludes some transformations that might be allowed if
floating-point behavior was less precisely specified. For example,
-x
cannot generally be replaced by (0.0 -
x)
because the sign of the result will differ if
x
is -0.0
. Also, other possibly
value-changing transformation such as replacing (a * b +
c)
with a call to a fused multiply-accumulate library method
are not allowed unless the result can be proven to be identical.
There are no circumstances where the evaluation of a floating-point expression may use intermediate results that have more precision or more exponent range than indicated by the expression's type.
A floating-point operation that overflows produces a signed infinity.
A floating-point operation that underflows produces a subnormal value or a signed zero.
A floating-point operation that has no unique mathematically defined result produces NaN.
All numeric operations with NaN as an operand produce NaN as a result.
Since NaN is unordered, any numerical comparison operation involving one
or two NaNs returns false
, any ==
comparison involving NaN
returns false
, and any !=
comparison involving NaN returns
true
.
Floating-point arithmetic is an approximation to real arithmetic. While there are an infinite number of real numbers, a particular floating-point format only has a finite number of values. In the Java programming language, a rounding policy is a function used to map from a real number to a floating-point value in a given format. For real numbers in the representable range of a floating-point format, a continuous segment of the real number line is mapped to a single floating-point value. The real number whose value is numerically equal to a floating-point value is mapped to that floating-point value; for example, the real number 1.5 is mapped to the floating-point value 1.5 in a given format. The Java programming language defines two rounding policies, as follows:
The round to nearest rounding policy applies to all floating-point operators except for (i) conversion to an integer value, and (ii) floating-point remainder. Under the round to nearest rounding policy, inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, then the value whose least significant bit is zero is chosen.
The round to nearest rounding policy corresponds to the default rounding-direction attribute for binary arithmetic in IEEE 754, roundTiesToEven.
The roundTiesToEven rounding-direction attribute was known as the "round to nearest" rounding mode in the 1985 version of the IEEE 754 Standard. The rounding policy in the Java programming language is named after this rounding mode.
The round toward zero rounding policy applies to (i) conversion of a floating-point value to an integer value (§5.1.3), and (ii) floating-point remainder (§15.17.3). Under the round toward zero rounding policy, inexact results are rounded to the nearest representable value that is not greater in magnitude than the infinitely precise result. For conversion to integer, the round toward zero rounding policy is equivalent to truncation where fractional significand bits are discarded.
The round toward zero rounding policy corresponds to the roundTowardZero rounding-direction attribute for binary arithmetic in IEEE 754.
The roundTowardZero rounding-direction attribute was known as the "round toward zero" rounding mode in the 1985 version of the IEEE 754 Standard. The rounding policy in the Java programming language is named after this rounding mode.
The Java programming language requires that every floating-point operator rounds its floating-point result to the result precision. The rounding policy used for each floating-point operator is either round to nearest or round toward zero, as specified above.
Java 1.0 and 1.1 required strict evaluation of
floating-point expressions. Strict evaluation means that each float
operand corresponds to a value representable in the IEEE 754
binary32 format, each double
operand corresponds to a value
representable in the IEEE 754 binary64 format, and each
floating-point operator with a corresponding IEEE 754 operation matches
the IEEE 754 result for the same operands.
Strict evaluation provides predictable results, but caused performance
problems in the Java Virtual Machine implementations for some processor families
common in the Java 1.0/1.1 era. Consequently, in Java 1.2 through Java
SE 16, the Java SE Platform allowed a Java Virtual Machine implementation to have one or
two value sets associated with each
floating-point type. The float
type was associated with the
float value set and the
float-extended-exponent value set, while the
double
type was associated with the double value
set and the double-extended-exponent value
set. The float value set corresponded to the values
representable in the IEEE 754 binary32 format; the
float-extended-exponent value set had the same number of precision
bits but larger exponent range. Similarly, the double value set
corresponded to the values representable in the IEEE 754 binary64
format; the double-extended-exponent value set had the same number of
precision bits but larger exponent range. Allowing use of the
extended-exponent value sets by default ameliorated the performance
problems on some processor families.
For compatibility, Java 1.2 allowed the programmer
to forbid an implementation from using the extended-exponent value
sets. The programmer expressed this by placing the strictfp
modifier
on the declaration of a class, interface, or method. strictfp
constrained the floating-point semantics of any enclosed expressions
to use the float value set for float
expressions and the double
value set for double
expressions, ensuring the results of such
expressions were fully predictable. Code modified by strictfp
thus
had the same floating-point semantics as specified in Java 1.0 and
1.1.
In Java SE 17 and later, the Java SE Platform always requires strict
evaluation of floating-point expressions. Newer members of the
processor families that had performance problems implementing strict
evaluation no longer have that difficulty. This specification no
longer associates float
and double
with the four value sets
described above, and the strictfp
modifier no longer affects the
evaluation of floating-point expressions. For compatibility,
strictfp
remains a keyword in Java SE 17 (§3.8)
and continues to have restrictions on its use (§8.4.3, §9.4), although Java
compilers are encouraged to warn the programmer about its obsolete
status. Future versions of the Java programming language may redefine or remove the
strictfp
keyword.
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 may be converted to some other reference type.
Casting (§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 operator. 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, cannot be converted to 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 types (§4.7). 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.4), array creation expression
(§15.10.2), method reference expression
(§15.13.3), array initializer expression
(§10.6), string concatenation operator
expression (§15.18.1), or lambda expression
(§15.27.4) throws an OutOfMemoryError
if there is
insufficient memory available.
An array creation expression (§15.10.2)
throws a NegativeArraySizeException
if the value of any
dimension expression is less than zero.
An array access expression (§15.10.4)
throws a NullPointerException
if the value of the array reference expression is
null
.
An array access expression (§15.10.4)
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 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
.
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 switch expression (§15.28) throws an
IncompatibleClassChangeError
if no switch label matches the value of the selector
expression.
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 (Threads and Locks)), 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:
class Test { public static void main(String[] args) { double d = 8E307; System.out.println(4.0 * d * 0.5); System.out.println(2.0 * d); } }
prints:
Infinity 1.6E308
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, object creations, field accesses, method invocations, method references, and array accesses. A parenthesized expression is also treated syntactically as a primary expression.
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.10.3) 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 grammar
presented here avoids the problem by keeping Name
and Primary separate and allowing either in certain other syntax
rules (those
for ClassInstanceCreationExpression,
MethodInvocation, ArrayAccess,
and PostfixExpression, though
not FieldAccess because it 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 shown here for convenience:
The type of a literal is determined as follows:
The type of an integer literal (§3.10.1)
that ends with L
or l
(ell) 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
(§4.2.3).
The type of any other floating-point literal is double
(§4.2.3).
The type of a boolean literal (§3.10.3)
is boolean
(§4.2.5).
The type of a string literal (§3.10.5)
or a text block (§3.10.6) is String
(§4.3.3).
The type of the null literal null
(§3.10.8)
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 type, or primitive type, or the
pseudo-type void
, followed by a '.
' and the token class
.
The TypeName must denote a class or interface that is accessible (§6.6). It is a compile-time error if the TypeName denotes a class or interface that is not accessible, or denotes a type variable.
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
>
.
A class literal evaluates to the Class
object for the named class,
interface, array type, or primitive 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 as an expression in the following
contexts:
in the body of an instance method of a class (§8.4.3.2)
in the body of a constructor of a class (§8.8.7)
in an instance initializer of a class (§8.6)
in the initializer of an instance variable of a class (§8.3.2)
in the body of an instance method of an interface,
that is, a default method or a non-static
private
interface method (§9.4)
When used as an expression, the keyword this
denotes a value
that is a reference either to the object for which the instance method
was invoked (§15.12), or to the
object being constructed. The value denoted by this
in a lambda body
(§15.27.2) is the same as the value denoted by
this
in the surrounding context.
The keyword this
is also used in explicit
constructor invocation statements (§8.8.7.1), and
to denote the receiver parameter of a method or constructor (§8.4).
It is a compile-time error if a this
expression occurs in a
static context (§8.1.3).
Let C by the innermost enclosing class or interface declaration of
a this
expression. If C is generic, with type parameters
F1,...,Fn, the type of this
is C<
F1,...,Fn>
.
Otherwise, the type of this
is C.
At run time, the class of the actual object referred to may be C or a subclass of C (§8.1.5.
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 n be an integer such that TypeName denotes the n'th
lexically enclosing class or interface declaration of the class or
interface whose declaration immediately encloses the qualified this
expression.
The value of a qualified this
expression TypeName.
this
is the n'th lexically enclosing instance of this
.
If TypeName denotes a generic class, with type parameters
F1,...,Fn, the type of the qualified this
expression is
TypeName<
F1,...,Fn>
. Otherwise, the type of the
qualified this
expression is TypeName.
It is a compile-time error if a qualified this
expression occurs in a
static context (§8.1.3).
It is a compile-time error if the class or interface whose declaration
immediately encloses a qualified this
expression is not an inner
class of TypeName or TypeName 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 affect whether a variable is definitely assigned,
definitely assigned when true
, definitely assigned when false
,
definitely unassigned, definitely unassigned when true
, or
definitely unassigned when false
(§16 (Definite Assignment)).
If a parenthesized expression appears in a context of a particular kind with target type T (§5 (Conversions and Contexts)), its contained expression similarly appears in a context of the same kind with target type T.
If the contained expression is a poly expression (§15.2), the parenthesized expression is also a poly expression. Otherwise, it is a standalone expression.
A poly parenthesized expression is compatible with a target type T if its contained expression is compatible with T.
A class instance creation expression is used to create new objects that are instances of classes.
The following production from §15.12 is shown here for convenience:
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 type arguments to a constructor but uses the diamond form for type arguments to the class.
This rule is introduced because inference of a generic class's type arguments may influence the constraints on a generic constructor's type arguments.
If
TypeArguments is present immediately after new
, or immediately
before (
, then it is a compile-time error if any of the type
arguments are wildcards (§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 expression or an ExpressionName.
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.
A class instance creation expression is a poly expression (§15.2) if it uses the diamond form for type arguments to the class, and it appears in an assignment context or an invocation context (§5.2, §5.3). Otherwise, it is a standalone expression.
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 the class to be instantiated (§15.9.1), the enclosing instances (if any) of the newly created instance (§15.9.2), and the constructor to be invoked to create the new instance (§15.9.3).
If ClassOrInterfaceTypeToInstantiate ends with TypeArguments
(rather than <>
), then ClassOrInterfaceTypeToInstantiate must
denote a well-formed parameterized type (§4.5),
or a compile-time error occurs.
If ClassOrInterfaceTypeToInstantiate ends with <>
, but the
class or interface denoted by the Identifier in
ClassOrInterfaceTypeToInstantiate is not generic, then a
compile-time error occurs.
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 unqualified, then:
The Identifier in ClassOrInterfaceTypeToInstantiate must denote either a class that is accessible, freely extensible (§8.1.1.2), and not an enum class, or an interface that is accessible and freely extensible (§9.1.1.4). Otherwise, a compile-time error occurs.
If the Identifier in ClassOrInterfaceTypeToInstantiate
denotes a class, C, then an anonymous direct subclass of C
is declared. If TypeArguments is present, then C has type
arguments given by TypeArguments; if <>
is present,
then C will have its type arguments inferred in
§15.9.3; otherwise, C has no type
arguments. The body of the subclass is the ClassBody given
in the class instance creation expression. The class being
instantiated is the anonymous subclass.
If the Identifier in ClassOrInterfaceTypeToInstantiate
denotes an interface, I, then an anonymous direct subclass of
Object
that implements I is declared. If TypeArguments is
present, then I has type arguments given by TypeArguments;
if <>
is present, then I will have its type arguments
inferred in §15.9.3; otherwise, I has no
type arguments. The body of the subclass is the ClassBody
given in the class instance creation expression. The class
being instantiated is the anonymous subclass.
If the class instance creation expression is qualified, then:
The Identifier in ClassOrInterfaceTypeToInstantiate must unambiguously denote an inner class that is accessible, freely extensible, not an enum class, and a member of the compile-time type of the Primary expression or the ExpressionName. Otherwise, a compile-time error occurs.
Let the Identifier in ClassOrInterfaceTypeToInstantiate
denote a class, C. An anonymous direct subclass of C is
declared. If TypeArguments is present, then C has type
arguments given by TypeArguments; if <>
is present,
then C will have its type arguments inferred in
§15.9.3; otherwise, C has no type
arguments. The body of the subclass is the ClassBody given
in the class instance creation expression. The class being
instantiated is the anonymous subclass.
If a class instance creation expression does not declare an anonymous class, then:
If the class instance creation expression is unqualified, then:
The Identifier in ClassOrInterfaceTypeToInstantiate must
denote a class that is accessible, non-abstract
, and not an
enum class. Otherwise, a compile-time error occurs.
The class being instantiated is specified by the Identifier in
ClassOrInterfaceTypeToInstantiate. If TypeArguments is
present, then the class has type arguments given by
TypeArguments; if <>
is present, then the class will
have its type arguments inferred in §15.9.3;
otherwise, the class has no type arguments.
If the class instance creation expression is qualified, then:
The ClassOrInterfaceTypeToInstantiate must unambiguously denote
an inner class that is accessible, non-abstract
, not an enum
class, and a member of the compile-time type of the Primary
expression or the ExpressionName.
The class being instantiated is specified by the Identifier in
ClassOrInterfaceTypeToInstantiate. If TypeArguments is
present, then the class has type arguments given by
TypeArguments; if <>
is present, then the class will
have its type arguments inferred in §15.9.3;
otherwise, the class has no type arguments.
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
(§8.1.3), determined as follows:
If C is an inner local class, 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, let O be the immediately enclosing class or interface declaration of C, and let U be the immediately enclosing class or interface declaration of the class instance creation expression.
If U is not an inner class of O or O itself, then a compile-time error occurs.
Let n be an integer such that O is the n'th lexically enclosing class or interface declaration of U.
The immediately enclosing instance of i
is the n'th
lexically enclosing instance of this
.
If C is an inner member class, then:
If the class instance creation expression is unqualified, then:
If the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, if C is not a member of any class whose declaration lexically encloses the class instance creation expression, then a compile-time error occurs.
Otherwise, let O be the innermost enclosing class declaration of which C is a member, and let U be the immediately enclosing class or interface declaration of the class instance creation expression.
If U is not an inner class of O or O itself, then a compile-time error occurs.
Let n be an integer such that O is the n'th lexically enclosing class or interface declaration of U
The immediately enclosing instance of i
is the
n'th lexically enclosing instance of this
.
If the class instance creation expression is qualified, then
the immediately enclosing instance of i
is the object that
is the value of the Primary expression or the
ExpressionName.
If C is an anonymous class, and its direct superclass S is an
inner class, then i
may have an immediately enclosing
instance with respect to S, determined as follows:
If S is an inner local class, then:
If S occurs in 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, let O be the immediately enclosing class or interface declaration of S, and let U be the immediately enclosing class or interface declaration of the class instance creation expression.
If U is not an inner class of O or O itself, then a compile-time error occurs.
Let n be an integer such that O is the n'th lexically enclosing class or interface declaration of U.
The immediately enclosing instance of i
with respect to
S is the n'th lexically enclosing instance of this
.
If S is an inner member class, then:
If the class instance creation expression is unqualified, then:
If the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, if S is not a member of any class whose declaration encloses the class instance creation expression, then a compile-time error occurs.
Otherwise, let O be the innermost enclosing class declaration of which S is a member, and let U be the immediately enclosing class or interface declaration of the class instance creation expression.
If U is not an inner class of O or O itself, then a compile-time error occurs.
Let n be an integer such that O is the n'th lexically enclosing class or interface declaration of U.
The immediately enclosing instance of i
with respect to S
is the n'th lexically enclosing instance of this
.
If the class instance creation expression is qualified, then
the immediately enclosing instance of i
with respect to
S is the object that is the value of the Primary
expression or the ExpressionName.
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 with direct superclass S, then:
If S is not an inner class, or if S is a local class that occurs in a static context, then the arguments to the constructor are the arguments in the argument list of the class instance creation expression, if any, in the order they appear in the expression.
Otherwise, the first argument to the constructor is the
immediately enclosing instance of i
with respect to S
(§15.9.2), and the subsequent arguments
to the constructor are the arguments in the argument list of
the class instance creation expression, if any, in the order
they appear in the class instance creation
expression.
If C is a local class or a private
inner member class, then
the arguments to the constructor are the arguments in the
argument list of the class instance creation expression, if any,
in the order they appear in the class instance creation
expression.
If C is a non-private
inner member class, then the first
argument to the constructor is the immediately enclosing
instance of i
(§8.8.1, §15.9.2), and
the subsequent arguments to its constructor are the arguments in
the argument list of the class instance creation expression, if
any, in the order they appear in the class instance creation
expression.
Otherwise, the arguments to the constructor are the arguments in the argument list of the class instance creation expression, if any, in the order they appear in the expression.
Second, a constructor of C and corresponding throws
clause and
return type are determined:
If the class instance creation expression does not use <>
,
then:
If C is not an anonymous class, then:
Let T be the type denoted by C followed by any class
type arguments in the expression. The process specified in
§15.12.2, modified to handle
constructors, is used to choose one of the constructors of
T and determine its throws
clause.
If there is no unique most-specific constructor in T that is both applicable and accessible (§6.6), then a compile-time error occurs (as in method invocations).
Otherwise, the return type corresponding to the chosen constructor is T.
If C is an anonymous class, then:
The process specified in §15.12.2,
modified to handle constructors, is used to choose one of
the constructors of the direct superclass type of C and
determine its throws
clause.
If there is no unique most-specific constructor in the direct superclass type of C that is both applicable and accessible, then a compile-time error occurs (as in method invocations).
Otherwise, C's anonymous constructor is chosen as the constructor of C (§15.9.5.1). Its body consists of an explicit constructor invocation (§8.8.7.1) of the constructor chosen in the direct superclass type of C.
The throws
clause of the chosen constructor includes the
exceptions in the throws
clause of the constructor chosen
in the direct superclass type of C.
The return type corresponding to the chosen constructor is the anonymous class type.
If the class instance creation expression uses <>
, then:
If C is not an anonymous class, let D be the same as C. If C is an anonymous class, let D be the superclass or superinterface of C named by the class instance creation expression.
If D is a class, let c1
...cn
be the constructors of class
D. If D is an interface, let c1
...cn
be a singleton list
(n = 1) containing the zero-argument constructor of the
class Object
.
A list of methods m1
...mn
is defined for the purpose of
overload resolution and type argument inference. 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 D, 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 D.
The type parameters of mj
are X1...Xp,Y1...Yq. The
bound of each type parameter, if any, is θj
applied to
the corresponding type parameter bound in D 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
.
The name of mj
is #m
, an automatically
generated name that is distinct from all constructor and
method names in D and is shared by m1
...mn
.
To choose a constructor, we temporarily consider m1
...mn
to
be members of D. One of m1
...mn
is chosen, as determined
by the class instance creation expression's argument
expressions, using the process specified in §15.12.2.
If there is no unique most specific method that is both applicable and accessible, then a compile-time error occurs.
Otherwise, where mj
is the chosen method:
If C is not an anonymous class, then cj
is chosen as the
constructor of C.
The throws
clause of the chosen constructor is the same as
the throws
clause determined for mj
.
The return type corresponding to the chosen constructor is
the return type determined for mj
(§15.12.2.6).
If C is an anonymous class, then C's anonymous
constructor is chosen as the constructor of C. Its body
consists of an explicit constructor invocation
(§8.8.7.1) of cj
.
The throws
clause of the chosen constructor includes the
exceptions in the throws
clause determined for mj
.
The return type corresponding to the chosen constructor is the anonymous class type.
If the class instance creation expression is a poly expression,
then its compatibility with a target type is as determined by
§18.5.2.1, using mj
as the selected
method m
.
Testing for compatibility with a target type may occur multiple times before making a final determination of the class instance creation expression's target type and the return type corresponding to the chosen constructor. For example, an enclosing method invocation expression may require testing the class instance creation expression for compatibility with different methods' formal parameter types.
If C is an anonymous class, then its direct superclass type or
direct superinterface type is the return type determined for mj
(§15.12.2.6).
It is a compile-time error if the direct superclass type or direct superinterface type, or any subexpression therein ("subexpression" includes type arguments of parameterized types, bounds of wildcard type arguments, and element types of array types, but excludes bounds of type variables), has one of the following forms:
It is a compile-time error if an argument to a class instance creation expression is not compatible with its target type, as derived from the invocation type (§15.12.2.6).
If the compile-time declaration is applicable by variable arity
invocation (§15.12.2.4), then where the last
formal parameter type of the invocation type of the constructor is
Fn[]
, it is a compile-time error if the type which is the
erasure of Fn is not accessible at the point of invocation.
The type of the class instance creation expression is the return type corresponding to the chosen constructor, as defined above.
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 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 is invoked. This results in invoking at least one constructor for each superclass of the class. This process can be directed by explicit constructor invocation statements (§8.8.7.1) and is specified 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, for which the out-of-memory condition is detected after evaluation of the dimension expressions (§15.10.2).
An anonymous class is implicitly declared by a class instance creation expression or by an enum constant that ends with a class body (§8.9.1).
An anonymous class is never abstract
(§8.1.1.1).
An anonymous class is never sealed
(§8.1.1.2),
and thus has no permitted direct subclasses (§8.1.6).
An anonymous class declared by a class instance creation expression is
never final
(§8.1.1.2).
An anonymous class declared by an enum constant is always final
.
An anonymous class being non-final
is relevant in
casting, in particular the narrowing reference conversion allowed for
the cast operator (§5.5). On the other hand, it
is not relevant to subclassing, because it is impossible to declare a
subclass of an anonymous class (an anonymous class cannot be named by
an extends
clause) despite the anonymous class being non-final
.
An anonymous class is always an inner class (§8.1.3).
Like a local class or interface (§14.3), an anonymous class is not a member of any package, class, or interface (§7.1, §8.5).
The direct superclass type or direct superinterface type of an
anonymous class declared by a class instance creation expression
is given by the expression (§15.9.1),
with type arguments inferred as necessary while choosing a
constructor (§15.9.3). If a direct superinterface
type is given, the direct superclass type is Object
.
The direct superclass type of an anonymous class declared by an enum constant is the type of the declaring enum class.
The ClassBody of the class instance creation expression or enum constant declares fields (§8.3), methods (§8.4), member classes (§8.5), member interfaces (§9.1.1.3), instance initializers (§8.6), and static initializers (§8.7) of the anonymous class. The constructor of an anonymous class is always implicit (§15.9.5.1).
If a class instance creation expression with a ClassBody
uses a diamond (<>
) for the type arguments of the class
to be instantiated, then for all non-private
methods declared in the
ClassBody, it is as if the method declaration is annotated
with @Override
(§9.6.4.4).
When <>
is used, the inferred type arguments may not be as
anticipated by the programmer. Consequently, the supertype of the
anonymous class may not be as anticipated, and methods declared in the
anonymous class may not override supertype methods as
intended. Treating such methods as if annotated with @Override
(if
they are not explicitly annotated with @Override
) helps avoid
silently incorrect programs.
An anonymous class cannot have an explicitly declared constructor. Instead, an anonymous constructor is implicitly declared for an anonymous class. The form of the anonymous constructor for 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 or enum constant that declares C.
The actual arguments to the class instance creation expression
or enum constant are used to determine a constructor x
of S,
as specified in §15.9.3. The type of
each formal parameter of the anonymous constructor is made
identical to the corresponding formal parameter of x
.
The anonymous constructor body consists of an explicit constructor
invocation (§8.8.7.1) of the form
super(...)
, where the actual arguments are the
formal parameters of the anonymous constructor, in the order they
were declared. The superclass constructor to be invoked is x
.
Otherwise, the first formal parameter of the anonymous constructor
represents the value of the immediately enclosing instance of
i
with respect to S (§15.9.2). The type
of this parameter is the class type that immediately encloses
the declaration of S.
The anonymous 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 corresponds to the n-1'th actual argument.
The actual arguments to the class instance creation expression
are used to determine a constructor x
of S, as specified in
§15.9.3. The type of each formal parameter
of the anonymous constructor is made identical to the
corresponding formal parameter of x
.
The anonymous constructor body consists of an explicit constructor
invocation of the form o.super(...)
,
where o
is the first formal parameter of the
anonymous constructor, and the actual arguments are the subsequent
formal parameters of the constructor, in the order they were
declared. The superclass constructor to be invoked is x
.
In all cases, the throws
clause of an anonymous constructor lists all
the checked exceptions thrown by the explicit constructor invocation
statement contained in the anonymous constructor, as specified in
§15.9.3, 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 x
).
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 (Arrays)).
The following production from §4.3 is shown here for convenience:
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.
The rules above imply that the element type in an array creation expression cannot be a parameterized type, unless all type arguments to the parameterized type are unbounded wildcards.
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). 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.2-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.2-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.2-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).
An array access expression refers to a variable that is a component of an array.
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). 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.
At run time, evaluation of an array access expression behaves as follows:
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.10.4-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.10.4-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.10.4-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 further part of the array
access occurs, and that includes the check as to whether the value of
the array reference expression is null
.
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
.
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, that is, 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
class variable initializer (§8.3.2) or
static initializer (§8.7),
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 an instance variable initializer (§8.3.2),
instance initializer (§8.6), or
constructor (§8.8),
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
may be used in the
locations within a class declaration that allow the keyword this
as an expression (§15.8.3).
It is a compile-time error if a field access expression using the
keyword super
appears in a static context (§8.1.3).
For a field access expression of the form super
.
Identifier:
For a field access expression of the form
T.
super
.
Identifier:
Suppose that a field access expression super
.
f
appears within
class C, and the immediate superclass of C is class S. If f
in
S is accessible from class C (§6.6), then
super
.
f
is treated as if it had been the expression
this
.
f
in the body of class S. Otherwise, a compile-time
error occurs.
Thus, super
.
f
can access the field f
that
is accessible in class S, even if that field is hidden by a
declaration of a field f
in class C.
Suppose that a field access expression T.
super
.
f
appears
within class C, and the immediate superclass of the class denoted by
T is a class whose fully qualified name is S. If f
in S is
accessible from C, then T.
super
.
f
is treated as if it
had been the expression this
.
f
in the body of class
S. Otherwise, a compile-time error occurs.
Thus, T.
super
.
f
can access the field
f
that is accessible in class S, even if that field is hidden by a
declaration of a field f
in class T.
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
has the same effect
as ((T2)this).x
when x
has
package access. 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.
(
[ArgumentList] )
.
[TypeArguments] Identifier
(
[ArgumentList] )
.
[TypeArguments] Identifier
(
[ArgumentList] )
.
[TypeArguments] Identifier
(
[ArgumentList] )
super
.
[TypeArguments] Identifier
(
[ArgumentList] )
.
super
.
[TypeArguments] Identifier
(
[ArgumentList] )
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 specified in §15.12.3.
The exception types that a method invocation expression can throw are specified in §11.2.1.
It is a compile-time error if the name to the left of the rightmost
".
" that occurs before the (
in a
MethodInvocation cannot be classified as a
TypeName or an ExpressionName (§6.5.2).
If TypeArguments is present to the left of Identifier, then it is a compile-time error if any of the type arguments are wildcards (§4.5.1).
A method invocation expression is a poly expression if all of the following are true:
The invocation appears in an assignment context or an invocation context (§5.2, §5.3).
If the invocation is qualified (that is, any form of MethodInvocation except for the first), then the invocation elides TypeArguments to the left of the Identifier.
The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a return type that mentions at least one of the method's type parameters.
Otherwise, the method invocation expression is a standalone expression.
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 type to search for definitions of methods of that name.
The name of the method is specified by the MethodName or Identifier which immediately precedes the left parenthesis of the MethodInvocation.
For the type to search, there are six cases to consider, depending on the form that precedes the left parenthesis of the MethodInvocation:
If the form is MethodName, that is, just an Identifier, then:
If the Identifier appears in the scope of a method declaration with that name (§6.3, §6.4.1), then:
If there is an enclosing class or interface declaration of
which that method is a member, let E be the innermost such
class or interface declaration. The type to search is
the type of E.
this
(§15.8.4).
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 method declaration may be in scope due to one or more single-static-import or static-import-on-demand declarations. There is no type to search, as the method to be invoked is determined later (§15.12.2.1).
If the form is TypeName .
[TypeArguments]
Identifier, then the type to search is the (possibly raw)
type denoted by TypeName.
If the form is ExpressionName .
[TypeArguments]
Identifier, then the type to search is the declared type T
of the variable denoted by ExpressionName 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 .
[TypeArguments] Identifier,
then let T be the type of the Primary expression. The type
to search 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
.
[TypeArguments] Identifier,
then the type to search is the direct superclass type of the class
whose declaration contains the method invocation.
Let E be the class or interface declaration immediately enclosing
the method invocation. It is a compile-time error if E is the class
Object
or an interface.
If the form is TypeName .
super
.
[TypeArguments] Identifier, then:
It is a compile-time error if TypeName denotes neither a class nor an interface.
If TypeName denote a class, C, then the type to search is the direct superclass type of C.
It is a compile-time error if C is not a lexically
enclosing class declaration of the
method invocation, or if C is the class Object
.
Let E be the class or interface declaration immediately
enclosing the method invocation. It is a compile-time error if
E is the class Object
.
Otherwise, TypeName denotes an interface, I.
Let E be the class or interface declaration immediately enclosing the method invocation. It is a compile-time error if I is not a direct superinterface of E, or if there exists some other direct superclass or direct superinterface of E, J, such that J is a subclass or subinterface of I.
The type to search is the type of I that is a direct superinterface type of E.
The TypeName .
super
syntax is overloaded:
traditionally, the TypeName refers to a lexically enclosing class
declaration, and the target is the superclass of this class, as if the
invocation were an unqualified super
in the lexically enclosing class
declaration.
class Superclass { void foo() { System.out.println("Hi"); } } class Subclass1 extends Superclass { void foo() { throw new UnsupportedOperationException(); } Runnable tweak = new Runnable() { void run() { Subclass1.super.foo(); // Gets the 'println' behavior } }; }
To support invocation of default methods in superinterfaces, the TypeName may also refer to a direct superinterface of the current class or interface, and the target is that superinterface.
interface Superinterface { default void foo() { System.out.println("Hi"); } } class Subclass2 implements Superinterface { void foo() { throw new UnsupportedOperationException(); } void tweak() { Superinterface.super.foo(); // Gets the 'println' behavior } }
No syntax supports a combination of these forms,
that is, invoking a superinterface method of a lexically enclosing
class declaration, as if the invocation were of the form
InterfaceName .
super
in the lexically
enclosing class declaration.
class Subclass3 implements Superinterface { void foo() { throw new UnsupportedOperationException(); } Runnable tweak = new Runnable() { void run() { Subclass3.Superinterface.super.foo(); // Illegal } }; }
A workaround is to introduce a private
method in
the lexically enclosing class declaration, that performs the interface
super
call.
The second step searches the type determined in the previous step for member methods. This step uses the name of the method and 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 the one used at run time to perform the method dispatch.
Certain argument expressions that contain implicitly typed lambda expressions (§15.27.1) or inexact method references (§15.13.1) are ignored by the applicability tests, because their meaning cannot be determined until the invocation's target type is selected. On the other hand, it is only argument expressions - not the invocation's target type - that influence the applicability tests, even if the method invocation expression is a poly expression.
The process of determining applicability begins by determining the potentially applicable methods (§15.12.2.1). Then, to ensure compatibility with the Java programming language prior to Java SE 5.0, the process continues in three phases:
The first phase 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 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 allows overloading to be combined with variable arity methods, boxing, and unboxing.
A method is applicable if it is applicable by one of strict invocation (the first phase, §15.12.2.2), loose invocation (the second phase, §15.12.2.3), or variable arity invocation (the third phase, §15.12.2.4). Deciding whether a method is applicable will, in the case of generic methods (§8.4.4), require an analysis of the type arguments. Type arguments may be passed explicitly or implicitly; if they are passed implicitly, then bounds of the type arguments must be inferred from the argument expressions (§18 (Type Inference)).
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 §15.12.2.5.
To check for applicability, the types of an invocation's arguments cannot, in general, be inputs to the analysis. This is because:
The arguments to a method invocation may be poly expressions.
Poly expressions cannot be typed in the absence of a target type.
Overload resolution has to be completed before the arguments' target types will be known.
Instead, the input to the applicability check is a list of the arguments themselves. The arguments can be checked for compatibility with potential target types, even if the ultimate types of the arguments are unknown.
Note that overload resolution is independent of a target type. This is for two reasons:
First, it makes the user model more accessible and less error-prone. The meaning of a method name (i.e., the declaration corresponding to the name) is too fundamental to the meaning of a program to depend on subtle contextual hints. (In contrast, other poly expressions may have different behavior depending on a target type; but the variation in behavior is always limited and essentially equivalent, while no such guarantees can be made about the behavior of an arbitrary set of methods that share a name and arity.)
Second, it allows other properties - such as whether or not the method is a poly expression (§15.12) or how to categorize a conditional expression (§15.25) - to depend on the meaning of the method name, even before a target type is known.
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 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 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 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 (Binary Compatibility) for a detailed discussion of binary compatibility and type evolution.
The type 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 form of the method invocation expression is MethodName - that is, a single Identifier - then the search for potentially applicable methods also examines all member methods that are imported by single-static-import declarations and static-import-on-demand declarations of the compilation unit where the method invocation occurs (§7.5.3, §7.5.4) and that are not shadowed at the point where the method invocation appears.
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
,
protected
, no modifier (package access), or private
) in the
member's declaration, and on the inheritance of the member by
the class or interface determined by compile-time step 1, and on
where the method invocation appears.
If the member is a fixed arity method with arity n, the arity of the method invocation is equal to n, and for all i (1 ≤ i ≤ n), the i'th argument of the method invocation is potentially compatible, as defined below, with the type of the i'th parameter of the method.
If the member is a variable arity method with arity n, then
for all i (1 ≤ i ≤ n-1), the i'th
argument of the method invocation is potentially
compatible with the type of the i'th parameter
of the method; and, where the nth parameter of the method
has type T[]
, one of the following is true:
The arity of the method invocation is equal to n, and
the nth argument of the method invocation is
potentially compatible with either T or T[]
.
The arity of the method invocation is m, where m > n, and for all i (n ≤ i ≤ m), the i'th argument of the method invocation is potentially compatible with T.
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.
An expression is potentially compatible with a target type according to the following rules:
A lambda expression (§15.27) is potentially compatible with a functional interface type T (§9.8) if all of the following are true:
The arity of the function type of T (§9.9) is the same as the arity of the lambda expression.
If the function type of T has a void
return, then the
lambda body is either a statement expression
(§14.8) or a void-compatible block
(§15.27.2).
If the function type of T has a (non-void
) return type,
then the lambda body is either an expression or a
value-compatible block (§15.27.2).
A method reference expression (§15.13) is potentially compatible with a functional interface type T if, where the arity of the function type of T is n, there exists at least one potentially applicable method when the method reference expression targets the function type with arity n (§15.13.1), and one of the following is true:
The method reference expression has the form ReferenceType
::
[TypeArguments] Identifier and at least
one potentially applicable method is either (i) static
and
supports arity n, or (ii) not static
and supports
arity n-1.
The method reference expression has some other form and at
least one potentially applicable method is not static
.
A lambda expression or a method reference expression is potentially compatible with a type variable if the type variable is a type parameter of the candidate method.
A parenthesized expression (§15.8.5) is potentially compatible with a type if its contained expression is potentially compatible with that type.
A conditional expression (§15.25) is potentially compatible with a type if each of its second and third operand expressions are potentially compatible with that type.
A switch expression (§15.28) is potentially compatible with a type if all of its result expressions are potentially compatible with that type.
A class instance creation expression, a method invocation expression, or an expression of a standalone form (§15.2) is potentially compatible with any type.
The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface target types. In some cases involving type argument inference, a lambda expression appearing as a method invocation argument cannot be properly typed until after overload resolution. These rules allow the form of the lambda expression to still be taken into account, discarding obviously incorrect target types that might otherwise cause ambiguity errors.
An
argument expression is considered pertinent to
applicability for a potentially applicable method m
unless it has one of the following forms:
An implicitly typed lambda expression (§15.27.1).
An inexact method reference expression (§15.13.1).
If m
is a generic method and the method invocation does not
provide explicit type arguments, an explicitly typed lambda
expression or an exact method reference expression for which the
corresponding target type (as derived from the signature of m
)
is a type parameter of m
.
An explicitly typed lambda expression whose body is an expression that is not pertinent to applicability.
An explicitly typed lambda expression whose body is a block, where at least one result expression is not pertinent to applicability.
A parenthesized expression (§15.8.5) whose contained expression is not pertinent to applicability.
A conditional expression (§15.25) whose second or third operand is not pertinent to applicability.
Let m
be a potentially applicable method
(§15.12.2.1) with arity n and formal
parameter types F1 ... Fn, and let e1
, ..., en
be the actual
argument expressions of the method invocation. Then:
If m
is a generic method and the method invocation does not
provide explicit type arguments, then the applicability of the
method is inferred as specified in §18.5.1.
If m
is a generic method and the method invocation provides
explicit type arguments, then let R1 ... Rp (p ≥ 1)
be the type parameters of m
, let Bl be the declared bound of
Rl (1 ≤ l ≤ p), and let U1, ..., Up be
the explicit type arguments given in the method invocation. Then
m
is applicable by strict invocation if
both of the following are true:
For 1 ≤ i ≤ n, if ei
is pertinent to
applicability then ei
is compatible in a strict invocation
context with Fi[
R1:=U1, ...,
Rp:=Up]
(§5.3).
If m
is not a generic method, then m
is applicable
by strict invocation if, for 1 ≤ i ≤
n, either ei
is compatible in a strict invocation context
with Fi (§5.3) or ei
is not pertinent
to applicability.
If no method applicable by strict invocation 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 strict invocation.
The meaning of an implicitly typed lambda expression or an inexact method reference expression is sufficiently vague prior to resolving a target type that arguments containing these expressions are not considered pertinent to applicability; they are simply ignored (except for their expected arity) until overload resolution is finished.
Let m
be a potentially applicable method
(§15.12.2.1) with arity n and formal
parameter types F1, ..., Fn, and let e1
, ..., en
be the actual
argument expressions of the method invocation. Then:
If m
is a generic method and the method invocation does not
provide explicit type arguments, then the applicability of the
method is inferred as specified in §18.5.1.
If m
is a generic method and the method invocation provides
explicit type arguments, then let R1 ... Rp (p ≥ 1)
be the type parameters of m
, let Bl be the declared bound of
Rl (1 ≤ l ≤ p), and let U1 ... Up be the
explicit type arguments given in the method invocation. Then m
is applicable by loose invocation if both
of the following are true:
For 1 ≤ i ≤ n, if ei
is pertinent to
applicability (§15.12.2.2) then ei
is
compatible in a loose invocation context with
Fi[
R1:=U1, ..., Rp:=Up]
(§5.3).
If m
is not a generic method, then m
is applicable
by loose invocation if, for 1 ≤ i ≤
n, either ei
is compatible in a loose invocation context
with Fi (§5.3) or ei
is not pertinent
to applicability.
If no method applicable by loose invocation 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 loose invocation.
Where a variable arity method has formal parameter types F1, ...,
Fn-1, Fn[]
, let the i'th variable arity
parameter type of the method be defined as follows:
Let m
be a potentially applicable method (§15.12.2.1)
with variable arity, let T1, ..., Tk be the first k variable
arity parameter types of m
, and let e1
, ..., ek
be the actual
argument expressions of the method invocation. Then:
If m
is a generic method and the method invocation does not
provide explicit type arguments, then the applicability of the
method is inferred as specified in §18.5.1.
If m
is a generic method and the method invocation provides
explicit type arguments, then let R1 ... Rp (p ≥ 1)
be the type parameters of m
, let Bl be the declared bound of
Rl (1 ≤ l ≤ p), and let U1 ... Up be the
explicit type arguments given in the method invocation. Then m
is applicable by variable arity invocation
if:
For 1 ≤ i ≤ k, if ei
is pertinent to
applicability (§15.12.2.2) then ei
is
compatible in a loose invocation context with
Ti[
R1:=U1, ..., Rp:=Up]
(§5.3).
If m
is not a generic method, then m
is applicable
by variable arity invocation if, for 1 ≤ i
≤ k, either ei
is compatible in a loose invocation
context with Ti (§5.3) or ei
is not
pertinent to applicability.
If no method applicable by variable arity invocation is found, then a compile-time error occurs.
Otherwise, the most specific method (§15.12.2.5) is chosen among the methods applicable by variable arity invocation.
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 error. In cases such as an explicitly typed lambda expression argument (§15.27.1) or a variable arity invocation (§15.12.2.4), some flexibility is allowed to adapt one signature to the other.
One applicable method m1
is more specific than
another applicable method m2
, for an invocation with argument
expressions e1
, ..., ek
, if any of the following are true:
m2
is generic, and m1
is inferred to be more specific than
m2
for argument expressions e1
, ..., ek
by
§18.5.4.
m2
is not generic, and m1
and m2
are applicable by strict
or loose invocation, and where m1
has formal parameter types
S1, ..., Sn and m2
has formal parameter types T1, ...,
Tn, the type Si is more specific than
Ti for argument ei
for all i (1 ≤ i ≤
n, n = k).
m2
is not generic, and m1
and m2
are applicable by
variable arity invocation, and where the first k variable
arity parameter types of m1
are S1, ..., Sk and the first
k variable arity parameter types of m2
are T1, ...,
Tk, the type Si is more specific than
Ti for argument ei
for all i (1 ≤ i ≤
k). Additionally, if m2
has k+1 parameters, then the
k+1'th variable arity parameter type
of m1
is a subtype of the k+1'th
variable arity parameter type of m2
.
The above conditions are the only circumstances under which one method may be more specific than another.
A type S is more specific than a type T
for any expression if S <:
T
(§4.10).
A functional interface type S is more specific
than a functional interface type T for an expression e
if all of
the following are true:
The interface of S is neither a superinterface nor a subinterface of the interface of T.
If S or T is an intersection type, it is not the case that any interface of S is a superinterface or a subinterface of any interface of T. (The "interfaces of" an intersection type refers here to the set of interfaces that appear as (possibly parameterized) interface types in the intersection.)
Let MTS be the function type of the capture of S, and let MTT be the function type of T. MTS and MTT must have the same type parameters (if any) (§8.4.4).
Let P1, ..., Pn be the formal parameter types of MTS,
adapted to the type parameters of MTT.
Let P1', ..., Pn' be the formal parameter types of the
function type of S (without capture), adapted to the type
parameters of MTT.
Let Q1, ..., Qn be the formal parameter types of MTT.
Then, for all i (1 ≤ i ≤ n), Qi
<:
Pi and Qi = Pi'.
Generally, this rule asserts that the formal parameter types derived from S and T are the same. But in the case in which S is a wildcard-parameterized type, the check is more complex in order to allow capture variables to occur in formal parameter types: first, each formal parameter type of T must be a subtype of the corresponding formal parameter type of the capture of S; second, after mapping the wildcards to their bounds (§9.9), the formal parameter types of the resulting function types are the same.
Let RS be the return type of MTS, adapted to the type parameters of MTT, and let RT be the return type of MTT. One of the following must be true:
e
is an explicitly typed lambda expression
(§15.27.1), and one of the following is
true:
RS and RT are functional interface types, and there is
at least one result expression, and RS is more specific
than RT for each result expression of e
.
The result expression of a lambda expression with a block body is defined in §15.27.2; the result expression of a lambda expression with an expression body is simply the body itself.
RS is a primitive type, and RT is a reference type, and
there is at least one result expression, and each result
expression of e
is a standalone expression
(§15.2) of a primitive type.
RS is a reference type, and RT is a primitive type, and
there is at least one result expression, and each result
expression of e
is either a standalone expression of a
reference type or a poly expression.
e
is an exact method reference expression
(§15.13.1), and one of the following is true:
RS is a primitive type, RT is a reference type, and the compile-time declaration for the method reference has a return type which is a primitive type.
RS is a reference type, RT is a primitive type, and the compile-time declaration for the method reference has a return type which is a reference type.
e
is a parenthesized expression, and one of these
conditions applies recursively to the contained expression.
e
is a conditional expression, and, for each of the second
and third operands, one of these conditions applies recursively.
e
is a switch
expression, and, for each of its result
expressions, one of these conditions applies recursively.
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 accessible and applicable 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 specified 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
signatures (§8.4.2), and exactly
one of the maximally specific methods is concrete
(that is, neither abstract
nor default), then it is the most
specific method.
Otherwise, if all the maximally specific methods have
override-equivalent signatures, and all the
maximally specific methods are abstract
or default, and the
declarations of these methods have the same erased parameter
types, and at least one maximally specific method
is preferred according to the rules below,
then the most specific method is chosen arbitrarily among the
subset of the maximally specific methods that are preferred. The most specific method
is then considered to be abstract
.
A maximally specific method is preferred if it has:
a signature that is a subsignature of every maximally specific method's signature; and
a return type R (possibly void
), where either R is the
same as every maximally specific method's return type, or
R is a reference type and is a subtype of every maximally
specific method's return type (after adapting for any type
parameters (§8.4.4) if the two methods
have the same signature).
If no preferred method exists according to the above rules, then a maximally specific method is preferred if it:
has a signature that is a subsignature of every maximally specific method's signature; and
is return-type-substitutable (§8.4.5) for every maximally specific method.
The thrown exception types of the most specific method are
derived from the throws
clauses of the maximally specific
methods, as follows:
If the most specific method is generic, the throws
clauses
are first adapted to the type parameters of the most
specific method (§8.4.4).
If the most specific method is not generic but at least one
maximally specific method is generic, the throws
clauses
are first erased.
Then, the thrown exception types include every type E which satisfies the following constraints:
These rules for deriving a single method type from a group of overloaded methods are also used to identify the function type of a functional interface (§9.9).
Otherwise, the method invocation is ambiguous, and a compile-time error occurs.
The invocation type of a most specific accessible
and applicable method is a method type (§8.2)
which expresses the target types of the invocation arguments, the
result (return type or void
) of the invocation, and the exception
types of the invocation. It is determined as follows:
If the chosen method is generic and the method invocation does not provide explicit type arguments, the invocation type is inferred as specified in §18.5.2.
In this case, if the method invocation expression is a poly expression, then its compatibility with a target type is as determined by §18.5.2.1.
Testing for compatibility with a target type may occur multiple times before making a final determination of the method invocation expression's target type and invocation type. For example, an enclosing method invocation expression may require testing the deeper method invocation expression for compatibility with different methods' formal parameter types.
If the chosen method is generic and the method invocation provides explicit type arguments, let Pi be the type parameters of the method and let Ti be the explicit type arguments provided for the method invocation (1 ≤ i ≤ p). Then:
If unchecked conversion was necessary for the method to be
applicable, then the invocation type's parameter types are
obtained by applying the substitution [
P1:=T1,
..., Pp:=Tp]
to the parameter types of the
method's type, and the invocation type's return type and
thrown types are given by the erasure of the return type and
thrown types of the method's type.
If unchecked conversion was not necessary for the method to
be applicable, then the invocation type is obtained by
applying the substitution [
P1:=T1, ...,
Pp:=Tp]
to the method's type.
If the chosen method is not generic, then:
If unchecked conversion was necessary for the method to be applicable, the parameter types of the invocation type are the parameter types of the method's type, and the return type and thrown types are given by the erasures of the return type and thrown types of the method's type.
Otherwise, if the chosen method is
the getClass
method of the class Object
(§4.3.2), the invocation type is the
same as the method's type, except that the return type is
Class
<
?
extends
|T|>
, where T is
the type that was searched, as determined by
§15.12.1, and |T| denotes the erasure
of T (§4.6).
Otherwise, the invocation type is the same as the method's type.
If there is a most specific method declaration for a method invocation, it is called the compile-time declaration for the method invocation.
It is a compile-time error if an argument to a method invocation is not compatible with its target type, as derived from the invocation type of the compile-time declaration.
If the compile-time declaration is applicable by variable arity
invocation, then where the last formal parameter type of the
invocation type of the method is Fn[]
, it is a compile-time
error if the type which is the erasure of Fn is not accessible
(§6.6) at the point of invocation.
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 or in the ForInit
or ForUpdate part of a for
statement), or a
compile-time error occurs. Such a method invocation produces no value
and so must be used only in a situation where a value is not needed.
In addition, whether the compile-time declaration is appropriate may depend on the form of the method invocation expression before the left parenthesis, as follows:
If the form is MethodName - that is, just an Identifier - and the compile-time declaration is an instance method, then:
It is a compile-time error if the method invocation occurs in a static context (§8.1.3).
Otherwise, let T be the class or interface to search (§15.12.1). It is a compile-time error if the innermost enclosing class or interface declaration of the method invocation is neither T nor an inner class of T.
If the form is TypeName .
[TypeArguments]
Identifier, then the compile-time declaration must be
static
, or a compile-time error occurs.
If the form is ExpressionName .
[TypeArguments]
Identifier or Primary .
[TypeArguments]
Identifier, then the compile-time declaration must not be a
static
method declared in an interface, or a compile-time
error occurs.
If the form is TypeName .
super
.
[TypeArguments] Identifier, then:
It is a compile-time error if the compile-time declaration
is abstract
.
It is a compile-time error if the method invocation occurs in a static context.
If TypeName denotes a class C, then if the class or interface declaration immediately enclosing the method invocation is not C or an inner class of C, a compile-time error occurs.
If TypeName denotes an interface, let E be the class or interface declaration immediately enclosing the method invocation. A compile-time error occurs if there exists a method, distinct from the compile-time declaration, that overrides (§9.4.1) the compile-time declaration from a direct superclass or direct superinterface of E.
In the case that a superinterface overrides a method
declared in a grandparent interface, this rule prevents the
child interface from "skipping" the override by simply
adding the grandparent to its list of direct
superinterfaces. The appropriate way to access functionality
of a grandparent is through the direct superinterface, and
only if that interface chooses to expose the desired
behavior. (Alternately, the programmer is free to define an
additional superinterface that exposes the desired behavior
with a super
method invocation.)
The compile-time parameter types and compile-time result are determined as follows:
If the compile-time declaration for the method invocation is not a signature polymorphic method, then:
The compile-time parameter types are the types of the formal parameters of the compile-time declaration.
The compile-time result is the result of the invocation type of the compile-time declaration (§15.12.2.6).
If the compile-time declaration for the method invocation is a signature polymorphic method, then:
The compile-time parameter types are the types of the actual
argument expressions. An argument expression which is the
null literal null
(§3.10.8) is
treated as having the type Void
.
The compile-time result is determined as follows:
If the signature polymorphic method is either void
or
has a return type other than Object
, the compile-time
result is the result of the invocation type of the compile-time
declaration (§15.12.2.6).
Otherwise, if the method invocation expression is an
expression statement, the compile-time result is void
.
Otherwise, if the method invocation expression is the operand of a cast expression (§15.16), the compile-time result is the erasure of the type of the cast expression (§4.6).
Otherwise, the compile-time result is the signature
polymorphic method's return type, Object
.
A method is signature polymorphic if all of the following are true:
It is declared in the java.lang.invoke.MethodHandle
class or the java.lang.invoke.VarHandle
class.
It has a single variable arity parameter (§8.4.1)
whose declared type is Object
[]
.
The following compile-time information is then associated with the method invocation for use at run time:
The qualifying class or interface of the method invocation (§13.1).
The number of parameters and the compile-time parameter types, 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 part of the method invocation before the
left parenthesis is of the form super
.
Identifier
or of the form TypeName .
super
.
Identifier,
then the invocation mode is super
.
Otherwise, if the qualifying class or interface of the method
invocation is in fact an interface, then the invocation mode is
interface
.
If the result of the invocation type of the compile-time declaration
is not void
, then the type of the method invocation expression is
obtained by applying capture conversion (§5.1.10)
to the return type of the invocation type of 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 six cases to consider, depending on the form of the method invocation:
If the form is MethodName - that is, just an Identifier - then:
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 of the class whose declaration immediately
contains the method invocation. The target reference is the
n'th lexically enclosing instance of this
.
It is a compile-time error if the n'th lexically
enclosing instance of this
does not exist.
If the form is TypeName .
[TypeArguments]
Identifier, then there is no target reference.
If form is ExpressionName .
[TypeArguments]
Identifier, then:
If the form is Primary .
[TypeArguments] Identifier
involved, then:
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 form is super
.
[TypeArguments] Identifier,
then the target reference is the value of this
.
If the form is TypeName .
super
.
[TypeArguments] Identifier, then if TypeName denotes a
class, the target reference is the value of
TypeName.
this
; otherwise, the target reference is the
value of this
.
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 paragraph is 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<String>
,
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 an
invocation context (§5.3), an unchecked
conversion 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 to give a
compile-time unchecked warning at this conversion. Oracle's reference
implementation of a Java compiler identifies this unchecked warning
as a more informative unchecked generic array creation.
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.
An implementation of the Java programming language must ensure, as part of linkage, that the class or interface Q is accessible:
If Q is in a different package than D,
and their packages are in the same module,
and Q is public
or protected
,
then Q is accessible.
If Q is in a different package than D,
and their packages are in different modules,
and Q's module exports Q's package to D's module,
and Q is public
or protected
, then Q is accessible.
If Q is protected
, it is necessarily a nested
class or interface, so at compile time, its accessibility is affected
by the accessibility of classes and interfaces enclosing its
declaration. However, during linkage, its accessibility is not
affected by the accessibility of classes and interfaces enclosing its
declaration. Moreover, during linkage, a protected
Q is as
accessible as a public
Q. These discrepancies between access
control at compile time (§6.6) and access control
at run time are due to limitations in the Java Virtual Machine.
The implementation must also ensure, during linkage, that the method
m
can still be found in Q or a superclass or superinterface of Q.
If m
cannot be found, then a NoSuchMethodError
(which is a subclass of IncompatibleClassChangeError
)
occurs. If m
can be found, then let C be the class or interface that
declares m
. The implementation must ensure, during linkage, that the
declaration of m
in C is accessible to D:
If m
is protected
, then m
is accessible iff (i) either D
is in the same package as C, or D is a subclass of C or C
itself; and (ii) if m
is a protected
instance method, then
Q must be a subclass of D or D itself.
This is the only place where Q is involved in
checks for m
, because a protected
instance method may only
be invoked via a qualifying class or interface that aligns with
the invoker's type.
If m
has package access, then m
is accessible iff D is
in the same package as C.
If m
is private
, then m
is accessible iff D is C,
or D encloses C, or C encloses D, or C and D are
both enclosed by a third class or interface.
If either Q or m
is not accessible, then an IllegalAccessError
occurs
(§12.3).
If the invocation mode is interface
, then the implementation must
check that the target reference class still implements the specified
interface. If the target reference class does not still implement the
interface, then an IncompatibleClassChangeError
occurs.
As in the previous section (§15.12.4.3):
Let Q be the qualifying class or interface of the method invocation (§13.1).
Let m
be the method found in Q or a superclass or
superinterface of Q. (Note that m
was merely the name of the
method in the previous section; here it is the actual declaration.)
The strategy for locating a method to invoke 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 or
interface C 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 three possibilities for the invocation mode are then
considered:
If the invocation mode is super
, overriding is not
allowed. Method m
of class or interface C is the one to
be invoked. If m
is abstract
, an AbstractMethodError
is thrown.
Otherwise, if the invocation mode is virtual
, and Q and
m
jointly indicate a signature polymorphic method
(§15.12.3), then the target object is
an instance of java.lang.invoke.MethodHandle
or java.lang.invoke.VarHandle
. The target
object encapsulates state which is matched against the
information associated with the method invocation at compile
time. Details of this matching are given in The Java Virtual Machine Specification, Java SE 17 Edition and the
Java SE Platform API. If matching succeeds, then either the method
referenced by the java.lang.invoke.MethodHandle
instance is directly and
immediately invoked, or the variable represented by the
java.lang.invoke.VarHandle
instance is directly and immediately accessed,
and in either case the procedure in
§15.12.4.5 is not executed.
If matching fails, then a java.lang.invoke.WrongMethodTypeException
is thrown.
Otherwise, the invocation mode is interface
or virtual
.
If the method m
of class or interface C is private
,
then it is the method to be invoked.
Otherwise, overriding may occur. A dynamic method lookup, specified below, is used to locate the method to invoke. The lookup procedure starts from class R, the actual run-time class of the target object.
Note that for invocation mode interface
, R
necessarily implements Q; for invocation mode virtual
, R
is necessarily either Q or a subclass of Q. If the target
object is an array, then R is a "class" representing an
array type.
The procedure for dynamic method lookup is as follows. Let S be the class to search, beginning with R. Then:
If class S contains a declaration for a method that overrides
method m
of class or interface C from R
(§8.4.8.1), then that overriding method is
the method to be invoked, and the procedure terminates.
Otherwise, if S has a superclass, then steps 1 and 2 of this lookup procedure are performed recursively using the direct superclass of S in place of S; the method to be invoked, if any, is the result of the recursive invocation of this lookup procedure.
If no method is found by the previous two steps, the superinterfaces of S are searched for a suitable method.
A set of candidate methods is considered with the following
properties:
(i) each method is declared in a (direct or indirect)
superinterface of R;
(ii) each method has the name and descriptor required by the
method invocation;
(iii) each method is non-static
and non-private
;
(iv) for each method, where the method's declaring interface is
I, there is no other method satisfying (i) through (iii) that
is declared in a subinterface of I.
If this set contains a default method, one such method is the
method to be invoked. Otherwise, an abstract
method in the set
is selected as the method to be invoked.
Dynamic method lookup may cause the following errors to occur:
If the method to be invoked is abstract
, an AbstractMethodError
is thrown.
If the method to be invoked is default
, and more than one
default method appears in the set of candidates in step 3 above,
an IncompatibleClassChangeError
is thrown.
If the invocation mode is interface
and the method to be
invoked is neither public
nor private
, an IllegalAccessError
is thrown.
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, as specified above;
additional details about the behavior of the Java Virtual Machine under these
circumstances are given by The Java Virtual Machine Specification, Java SE 17 Edition.
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 invocation conversion (§5.3).
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.6, §5.1.9, §8.4.1, §8.4.8.3, §15.13.2, §15.12.4.2, §15.27.3).
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.
A method reference expression is used to refer to the invocation of a method without actually performing the invocation. Certain forms of method reference expression also allow class instance creation (§15.9) or array creation (§15.10) to be treated as if it were a method invocation.
::
[TypeArguments] Identifier ::
[TypeArguments] Identifier ::
[TypeArguments] Identifier super
::
[TypeArguments] Identifier .
super
::
[TypeArguments] Identifier ::
[TypeArguments] new
::
new
If TypeArguments is present to the right of ::
, then it is
a compile-time error if any of the type arguments are wildcards
(§4.5.1).
If a method reference expression has the form ExpressionName
::
[TypeArguments] Identifier or Primary
::
[TypeArguments] Identifier, it is a compile-time
error if the type of the ExpressionName or Primary is not a
reference type.
If a method reference expression has the form super
::
[TypeArguments] Identifier, let E be the class or interface
declaration immediately enclosing the method reference expression.
It is a compile-time error if E is the class Object
or if E
is an interface.
If a method reference expression has the form TypeName .
super
::
[TypeArguments] Identifier, then:
If TypeName denotes a class, C, then it is a compile-time
error if C is not a lexically enclosing class of the current
class, or if C is the class Object
.
If TypeName denotes an interface, I, then let E be the class or interface declaration immediately enclosing the method reference expression. It is a compile-time error if I is not a direct superinterface of E, or if there exists some other direct superclass or direct superinterface of E, J, such that J is a subclass or subinterface of I.
If TypeName denotes a type variable, then a compile-time error occurs.
If a method reference expression has the form super
::
[TypeArguments] Identifier or TypeName .
super
::
[TypeArguments] Identifier, it is a compile-time
error if the expression occurs in a static context
(§8.1.3).
If a method reference expression has the form ClassType ::
[TypeArguments] new
, then:
ClassType must name a class that is accessible
(§6.6), non-abstract
, and not an enum
class, or a compile-time error occurs.
If ClassType denotes a parameterized type (§4.5), then it is a compile-time error if any of its type arguments are wildcards.
If ClassType denotes a raw type (§4.8),
then it is a compile-time error if TypeArguments is present
after the ::
.
If a method reference expression has the form ArrayType ::
new
, then ArrayType must denote a type that is reifiable
(§4.7), or a compile-time error occurs.
The target reference of an instance method
(§15.12.4.1) may be provided by the method
reference expression using an ExpressionName, a Primary, or
super
, or it may be provided later when the method is invoked. The
immediately enclosing instance of a new inner class instance
(§15.9.2) is provided by a
lexically enclosing instance of this
(§8.1.3).
When more than one member method of a type has the same name, or when a class has more than one constructor, the appropriate method or constructor is selected based on the functional interface type targeted by the method reference expression, as specified in §15.13.1.
If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly. Similarly, the type arguments of a generic type mentioned by the method reference expression may be provided explicitly or inferred.
Method reference expressions are always poly expressions (§15.2).
It is a compile-time error if a method reference expression occurs in a program in someplace other than an assignment context (§5.2), an invocation context (§5.3), or a casting context (§5.5).
Evaluation of a method reference expression produces an instance of a functional interface type (§9.8). This does not cause the execution of the corresponding method; instead, the execution may occur at a later time when an appropriate method of the functional interface is invoked.
Here are some method reference expressions, first with no target reference and then with a target reference:
String::length // instance method System::currentTimeMillis // static method List<String>::size // explicit type arguments for generic type List::size // inferred type arguments for generic type int[]::clone T::tvarMember System.out::println "abc"::length foo[x]::bar (test ? list.replaceAll(String::trim) : list) :: iterator super::toString
Here are some more method reference expressions:
String::valueOf // overload resolution needed Arrays::sort // type arguments inferred from context Arrays::<String>sort // explicit type arguments
Here are some method reference expressions that represent a deferred creation of an object or an array:
ArrayList<String>::new // constructor for parameterized type ArrayList::new // inferred type arguments // for generic class Foo::<Integer>new // explicit type arguments // for generic constructor Bar<String>::<Integer>new // generic class, generic constructor Outer.Inner::new // inner class constructor int[]::new // array creation
It is not possible to specify a particular signature
to be matched, for example, Arrays::sort(int[])
.
Instead, the functional interface provides argument types that are
used as input to the overload resolution algorithm
(§15.12.2). This should satisfy the vast majority
of use cases; when the rare need arises for more precise control, a
lambda expression can be used.
The use of type argument syntax in the class name
before a delimiter (List<String>::size
)
raises the parsing problem of distinguishing between <
as a
type argument bracket and <
as a less-than operator. In theory,
this is no worse than allowing type arguments in cast expressions;
however, the difference is that the cast case only comes up when a
(
token is encountered; with the addition of method reference
expressions, the start of every expression is
potentially a parameterized type.
The compile-time declaration of a method reference expression is the method to which the expression refers. In special cases, the compile-time declaration does not actually exist, but is a notional method that represents a class instance creation or an array creation. The choice of compile-time declaration depends on a function type targeted by the expression, just as the compile-time declaration of a method invocation depends on the invocation's arguments (§15.12.3).
The search for a compile-time declaration mirrors the process for method invocations in §15.12.1 and §15.12.2, as follows:
First, a type to search is determined:
If the method reference expression has the form
ExpressionName ::
[TypeArguments]
Identifier or Primary ::
[TypeArguments]
Identifier, the type to search is the type of the
expression preceding the ::
token.
If the method reference expression has the form
ReferenceType ::
[TypeArguments]
Identifier, the type to search is the result of capture
conversion (§5.1.10) applied to
ReferenceType.
If the method reference expression has the form super
::
[TypeArguments] Identifier, the type to
search is the superclass type of the immediately enclosing class
or interface declaration of the method reference expression.
Let T be the class or interface declaration immediately
enclosing the method reference expression. It is a compile-time
error if T is the class Object
or an interface.
If the method reference expression has the form TypeName
.
super
::
[TypeArguments] Identifier,
then if TypeName denotes a class, the type to search is
the superclass type of the named class; otherwise,
TypeName denotes an interface to search.
It is a compile-time error if TypeName is neither a lexically enclosing class or interface declaration of the method reference expression, nor a direct superinterface of the immediately enclosing class or interface declaration of the method reference expression.
It is a compile-time error if TypeName is the class Object.
It is a compile-time error if TypeName is an interface, and there exists some other direct superclass or direct superinterface of the immediately enclosing class or interface declaration of the method reference expression, J, such that J is a subclass or subinterface of TypeName.
For the two other forms (involving ::
new
), the
referenced method is notional and there is no type to
search.
Second, given a targeted function type with n parameters, a set of potentially applicable methods is identified:
If the method reference expression has the form
ReferenceType ::
[TypeArguments]
Identifier, then the potentially applicable methods are:
the member methods of the type to search that would be potentially applicable (§15.12.2.1) for a method invocation which names Identifier, has arity n, has type arguments TypeArguments, and appears in the same class as the method reference expression; plus
the member methods of the type to search that would be potentially applicable for a method invocation which names Identifier, has arity n-1, has type arguments TypeArguments, and appears in the same class as the method reference expression.
Two different arities, n and n-1,
are considered, to account for the possibility that this form
refers to either a static
method or an instance method.
If the method reference expression has the form ClassType
::
[TypeArguments] new
, then the potentially
applicable methods are a set of notional methods
corresponding to the constructors of ClassType.
If ClassType is a raw type, but is not a non-static
member type of a raw type, the candidate notional member
methods are those specified in §15.9.3
for a class instance creation expression that uses <>
to elide the type arguments to a class. Otherwise, the
candidate notional member methods are the constructors of
ClassType, treated as if they were methods with return
type ClassType.
Among these candidates, the potentially applicable methods are the notional methods that would be potentially applicable for a method invocation which has arity n, has type arguments TypeArguments, and appears in the same class as the method reference expression.
If the method reference expression has the form ArrayType
::
new
, a single notional method is
considered. The method has a single parameter of type int
,
returns the ArrayType, and has no throws
clause. If
n = 1, this is the only potentially applicable method;
otherwise, there are no potentially applicable
methods.
For all other forms, the potentially applicable methods are the member methods of the type to search that would be potentially applicable for a method invocation which names Identifier, has arity n, has type argument TypeArguments, and appears in the same class as the method reference expression.
Finally, if there are no potentially applicable methods, then there is no compile-time declaration.
Otherwise, given a targeted function type with parameter types P1, ..., Pn and a set of potentially applicable methods, the compile-time declaration is selected as follows:
If the method reference expression has the form
ReferenceType ::
[TypeArguments]
Identifier, then two searches for a most specific
applicable method are performed. Each search is as specified
in §15.12.2.2 through
§15.12.2.5, with the clarifications
below. Each search produces a set of applicable methods and,
possibly, designates a most specific method of the set. In
the case of an error as specified in
§15.12.2.4, the set of applicable
methods is empty. In the case of an error as specified in
§15.12.2.5, there is no most specific
method.
In the first search, the method reference is treated as if it were an invocation with argument expressions of types P1, ..., Pn. Type arguments, if any, are given by the method reference expression.
In the second search, if P1, ..., Pn is not empty and
P1 is a subtype of ReferenceType, then the method
reference expression is treated as if it were a method
invocation expression with argument expressions of types
P2, ..., Pn. If ReferenceType is a raw type, and there
exists a parameterization of this type,
G<
...>
, that is a supertype of P1, the
type to search is the result of capture conversion
(§5.1.10) applied to
G<
...>
; otherwise, the type to search is the
same as the type of the first search. Type arguments, if
any, are given by the method reference expression.
If the first search produces a most specific method that is
static
, and the set of applicable methods produced by the
second search contains no non-static
methods, then the
compile-time declaration is the most specific method of the
first search.
Otherwise, if the set of applicable methods produced by the
first search contains no static
methods, and the second
search produces a most specific method that is non-static
,
then the compile-time declaration is the most specific
method of the second search.
For all other forms of method reference expression, one search for a most specific applicable method is performed. The search is as specified in §15.12.2.2 through §15.12.2.5, with the clarifications below.
The method reference is treated as if it were an invocation with argument expressions of types P1, ..., Pn; the type arguments, if any, are given by the method reference expression.
If the search results in an error as specified in
§15.12.2.2 through
§15.12.2.5, or if the most specific
applicable method is static
, there is no compile-time
declaration.
Otherwise, the compile-time declaration is the most specific applicable method.
It is a compile-time error if a method reference expression has the
form ReferenceType ::
[TypeArguments] Identifier,
and the compile-time declaration is static
, and ReferenceType is
not a simple or qualified name (§6.2).
It is a compile-time error if the method reference expression has the
form super
::
[TypeArguments] Identifier or
TypeName .
super
::
[TypeArguments]
Identifier, and the compile-time declaration is abstract
.
It is a compile-time error if the method reference expression has the
form super
::
[TypeArguments] Identifier or
TypeName .
super
::
[TypeArguments]
Identifier, and the method reference expression occurs in a static
context (§8.1.3).
It is a compile-time error if the method reference expression has the
form TypeName .
super
::
[TypeArguments]
Identifier, and TypeName denotes a class C, and the immediately
enclosing class or interface declaration of the method reference
expression is not C or an inner class of C.
It is a compile-time error if the method reference expression has the
form TypeName .
super
::
[TypeArguments]
Identifier, and TypeName denotes an interface, and there exists a
method, distinct from the compile-time declaration, that overrides
the compile-time declaration from a direct superclass or direct
superinterface of the class or interface whose declaration immediately
encloses the method reference expression (§8.4.8,
§9.4.1).
It is a compile-time error if the method reference expression is of
the form ClassType ::
[TypeArguments] new
and a
compile-time error would occur when determining an enclosing instance
for ClassType as specified in §15.9.2 (treating
the method reference expression as if it were an unqualified class
instance creation expression).
A method reference expression of the form
ReferenceType ::
[TypeArguments] Identifier can be
interpreted in different ways. If Identifier refers to an instance
method, then the implicit lambda expression has an extra parameter
compared to if Identifier refers to a static
method. It is
possible for ReferenceType to have both kinds of applicable methods,
so the search algorithm described above identifies them separately,
since there are different parameter types for each case.
An example of ambiguity is:
interface Fun<T,R> { R apply(T arg); } class C { int size() { return 0; } static int size(Object arg) { return 0; } void test() { Fun<C, Integer> f1 = C::size; // Error: instance method size() // or static method size(Object)? } }
This ambiguity cannot be resolved by providing an
applicable instance method which is more specific than an applicable
static
method:
interface Fun<T,R> { R apply(T arg); } class C { int size() { return 0; } static int size(Object arg) { return 0; } int size(C arg) { return 0; } void test() { Fun<C, Integer> f1 = C::size; // Error: instance method size() // or static method size(Object)? } }
The search is smart enough to ignore ambiguities in which all the applicable methods (from both searches) are instance methods:
interface Fun<T,R> { R apply(T arg); } class C { int size() { return 0; } int size(Object arg) { return 0; } int size(C arg) { return 0; } void test() { Fun<C, Integer> f1 = C::size; // OK: reference is to instance method size() } }
For convenience, when the name of a generic type is
used to refer to an instance method (where the receiver becomes the
first parameter), the target type is used to determine the type
arguments. This facilitates usage like Pair::first
in place of Pair<String,Integer>::first
.
Similarly, a method reference like Pair::new
is
treated like a "diamond" instance creation (new
Pair<>()
). Because the "diamond" is implicit, this
form does not instantiate a raw type; in fact,
there is no way to express a reference to the constructor of a raw
type.
For some method reference expressions, there is only one possible compile-time declaration with only one possible invocation type (§15.12.2.6), regardless of the targeted function type. Such method reference expressions are said to be exact. A method reference expression that is not exact is said to be inexact.
A method reference expression ending with Identifier is exact if it satisfies all of the following:
If the method reference expression has the form ReferenceType
::
[TypeArguments] Identifier, then
ReferenceType does not denote a raw type.
The type to search has exactly one member method with the name Identifier that is accessible to the class or interface in which the method reference expression appears.
This method is not variable arity (§8.4.1).
If this method is generic (§8.4.4), then the method reference expression provides TypeArguments.
A method
reference expression of the form ClassType ::
[TypeArguments] new
is exact if it satisfies all of the
following:
The type denoted by ClassType is not raw, or is a non-static
member type of a raw type.
The type denoted by ClassType has exactly one constructor that is accessible to the class or interface in which the method reference expression appears.
If this constructor is generic, then the method reference expression provides TypeArguments.
A method
reference expression of the form ArrayType ::
new
is
always exact.
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
The ground target type is derived from T as follows:
If T is a wildcard-parameterized functional interface type, then the ground target type is the non-wildcard parameterization (§9.9) of T.
A method reference expression is congruent with a function type if both of the following are true:
The function type identifies a single compile-time declaration corresponding to the reference.
The result of the function type is R, and the result of
applying capture conversion (§5.1.10)
to the return type of the invocation type
(§15.12.2.6) of the chosen compile-time
declaration is R' (where R is the target type that may
be used to infer R'), and neither R nor R' is void
,
and R' is compatible with R in an assignment context.
If unchecked conversion was necessary for the compile-time declaration
to be applicable, and this conversion would cause an unchecked warning
in an invocation context, then a compile-time unchecked warning occurs,
unless suppressed by @SuppressWarnings
(§9.6.4.5).
If unchecked conversion was necessary for the return type R',
described above, to be compatible with the function type's return
type, R, and this conversion would cause an unchecked warning in an
assignment context, then a compile-time unchecked warning occurs,
unless suppressed by @SuppressWarnings
.
If a method reference expression is compatible with a target type T, then the type of the expression, U, is the ground target type derived from T.
It is a compile-time error if any class or interface mentioned by either U or the function type of U is not accessible (§6.6) from the class or interface in which the method reference expression appears.
For each non-static
member method m
of U, if the function type
of U has a subsignature of the signature of m
, then a notional
method whose method type is the function type of U is said to
override m
, and any compile-time error or unchecked warning
specified in §8.4.8.3 may occur.
For each checked exception type X listed in the throws
clause of
the invocation type of the compile-time declaration, X or a
superclass of X must be mentioned in the throws
clause of the
function type of U, or a compile-time error occurs.
The key idea driving the compatibility definition
is that a method reference is compatible if and only if the equivalent
lambda expression (x, y, z)
is compatible. (This is informal, and
there are issues that make it difficult or impossible to formally
define the semantics in terms of such a rewrite.)
->
exp.<T1,
T2>method(x, y, z)
These compatibility rules provide a convenient facility for converting from one functional interface to another:
Task t = () ->
System.out.println("hi");
Runnable r = t::invoke;
The implementation may be optimized so that when a lambda-derived object is passed around and converted to various types, this does not result in many levels of adaptation logic around the core lambda body.
Unlike a lambda expression, a method reference can be congruent with a generic function type (that is, a function type that has type parameters). This is because the lambda expression would need to be able to declare type parameters, and no syntax supports this; while for a method reference, no such declaration is necessary. For example, the following program is legal:
interface ListFactory { <T> List<T> make(); } ListFactory lf = ArrayList::new; List<String> ls = lf.make(); List<Number> ln = lf.make();
At run time, evaluation of a method reference expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a method reference expression is distinct from invocation of the method itself.
First, if the method reference expression begins with an
ExpressionName or a Primary, this subexpression is evaluated. If
the subexpression evaluates to null
, a NullPointerException
is raised, and the
method reference expression completes abruptly. If the subexpression
completes abruptly, the method reference expression completes abruptly
for the same reason.
Next, either a new instance of a class with the properties below is
allocated and initialized, or an existing instance of a class with the
properties below is referenced. If a new instance is to be created,
but there is insufficient space to allocate the object, evaluation of
the method reference expression completes abruptly by throwing an
OutOfMemoryError
.
The value of a method reference expression is a reference to an instance of a class with the following properties:
The class implements the targeted functional interface type and, if the target type is an intersection type, every other interface type mentioned in the intersection.
Where the method reference expression has type U, for each
non-static
member method m
of U:
If the function type of U has a subsignature of the signature
of m
, then the class declares an invocation
method that overrides m
. The invocation method's
body invokes the referenced method, creates a class instance, or
creates an array, as described below. If the invocation method's
result is not void
, then the body returns the result of the
method invocation or object creation, after any necessary
assignment conversions (§5.2).
If the erasure of the type of a method being overridden differs
in its signature from the erasure of the function type of U,
then before the method invocation or object creation, an
invocation method's body checks that each argument value is an
instance of a subclass or subinterface of the erasure of the
corresponding parameter type in the function type of U; if
not, a ClassCastException
is thrown.
The class overrides no other methods of the functional interface
type or other interface types mentioned above, although it may
override methods of the Object
class.
The body of an invocation method depends on the form of the method reference expression, as follows:
If the form is ExpressionName ::
[TypeArguments]
Identifier or Primary ::
[TypeArguments]
Identifier, then the body of the invocation method has the
effect of a method invocation expression for a compile-time
declaration which is the compile-time declaration of the method
reference expression. Run-time evaluation of the method
invocation expression is as specified in
§15.12.4.3,
§15.12.4.4, and
§15.12.4.5, where:
The invocation mode is derived from the compile-time declaration as specified in §15.12.3.
The target reference is the value of ExpressionName or Primary, as determined when the method reference expression was evaluated.
The arguments to the method invocation expression are the formal parameters of the invocation method.
If the form is ReferenceType ::
[TypeArguments]
Identifier, the body of the invocation method similarly has
the effect of a method invocation expression for a compile-time
declaration which is the compile-time declaration of the method
reference expression. Run-time evaluation of the method
invocation expression is as specified in
§15.12.4.3,
§15.12.4.4, and
§15.12.4.5, where:
The invocation mode is derived from the compile-time declaration as specified in §15.12.3.
If the compile-time declaration is an instance method, then the target reference is the first formal parameter of the invocation method. Otherwise, there is no target reference.
If the compile-time declaration is an instance method, then the arguments to the method invocation expression (if any) are the second and subsequent formal parameters of the invocation method. Otherwise, the arguments to the method invocation expression are the formal parameters of the invocation method.
If the form is super
::
[TypeArguments]
Identifier or TypeName .
super
::
[TypeArguments] Identifier, the body of the invocation
method has the effect of a method invocation expression for a
compile-time declaration which is the compile-time declaration
of the method reference expression. Run-time evaluation of the
method invocation expression is as specified in
§15.12.4.3,
§15.12.4.4, and
§15.12.4.5, where:
If the method reference expression begins with a TypeName
that names a class, the target reference is the value of
TypeName .
this
at the point at which the method
reference is evaluated. Otherwise, the target reference is
the value of this
at the point at which the method
reference is evaluated.
The arguments to the method invocation expression are the formal parameters of the invocation method.
If the form is ClassType ::
[TypeArguments]
new
, the body of the invocation method has the effect of a
class instance creation expression of the form new
[TypeArguments] ClassType(A1, ..., An), where the
arguments A1, ..., An are the formal parameters of the
invocation method, and where:
If the form
is Type[]
k
::
new
(k ≥ 1), then the body of the
invocation method has the same effect as an array creation
expression of the form new
Type [
size
]
[]
k-1,
where size is the invocation method's
single parameter. (The notation
[]
k
indicates a sequence of k bracket pairs.)
If the body of the invocation method has the effect of a method
invocation expression, then the compile-time parameter types and the
compile-time result of the method invocation are determined as
specified in §15.12.3. For the purpose of
determining the compile-time result, the method invocation expression
is an expression statement if the invocation method's result is
void
, and the Expression of a return
statement if the invocation
method's result is non-void
.
The timing of method reference expression evaluation
is more complex than that of lambda expressions
(§15.27.4). When a method reference expression
has an expression (rather than a type) preceding the ::
separator, that subexpression is evaluated immediately. The result of
evaluation is stored until the method of the corresponding functional
interface type is invoked; at that point, the result is used as the
target reference for the invocation. This means the expression
preceding the ::
separator is evaluated only when the
program encounters the method reference expression, and is not
re-evaluated on subsequent invocations on the functional interface
type.
It is interesting to contrast the treatment of
null
here with its treatment during method invocation. When a method
invocation expression is evaluated, it is possible for the Primary
that qualifies the invocation to evaluate to null
but for no NullPointerException
to be raised. This occurs when the invoked method is static
(despite
the syntax of the invocation suggesting an instance method). Since the
applicable method for a method reference expression qualified by a
Primary is prohibited from being static
(§15.13.1), the evaluation of the method
reference expression is simpler - a null
Primary always raises a
NullPointerException
.
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.
The rules for evaluating expression names are given in §6.5.6.
A postfix expression followed by a ++
operator
is a postfix increment expression.
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) 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).
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.
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) 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).
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. A unary expression is either
a unary operator applied to an operand, or a switch
expression
(§15.28).
Expressions with unary operators group right-to-left, so
that -~x
means the same
as -(~x)
.
This portion of the grammar contains some tricks to avoid two potential syntactic ambiguities.
The first potential ambiguity would arise in
expressions such as (p)+q
, which looks, to a C or
C++ programmer, as though it could be either a cast to type
p
of a unary +
operating on
q
, or a binary addition of two quantities
p
and q
. In C and C++, the
parser handles this problem by performing a limited amount of
semantic analysis as it parses, so that it knows whether
p
is the name of a type or the name of a variable.
Java takes a different approach. The result of the
+
operator must be numeric, and all type names involved in casts
on numeric values are known keywords. Thus, if p
is
a keyword naming a primitive type, then (p)+q
can
make sense only as a cast of a unary expression. However, if
p
is not a keyword naming a primitive type, then
(p)+q
can make sense only as a binary arithmetic
operation. Similar remarks apply to the -
operator. The grammar
splits CastExpression into multiple cases to make
this distinction:
The nonterminal UnaryExpression
includes all unary operators, but the nonterminal
UnaryExpressionNotPlusMinus excludes uses of
all unary operators that could also be binary operators, which
in Java are +
and -
.
The second potential ambiguity is that the
expression (p)++
could, to a C or C++ programmer,
appear to be either a postfix increment of a parenthesized expression
or the beginning of a cast, for example,
in (p)++q
. As before, parsers for C and C++ know
whether p
is the name of a type or the name of a
variable. But a parser using only one-token lookahead and no semantic
analysis during the parse would not be able to tell, when ++
is the lookahead token, whether (p)
should be
considered a Primary expression or left alone for later
consideration as part of a CastExpression.
In Java, the result of the ++
operator must
be numeric, and all type names involved in casts on numeric values are
known keywords. Thus, if p
is a keyword naming a
primitive type, then (p)++
can make sense only as a
cast of a prefix increment expression, and there had better be an
operand such as q
following the
++
. However, if p
is not a keyword naming a
primitive type, then (p)++
can make sense only as a
postfix increment of p
. Similar remarks apply to
the --
operator. The nonterminal
UnaryExpressionNotPlusMinus therefore
also excludes uses of the prefix operators ++
and
--
.
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) 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).
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) 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).
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) 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) is performed on the operand.
The type of the unary minus expression is the promoted type of the operand.
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).)
The Java programming language has not adopted the stronger requirement from the 2019 version of the IEEE 754 Standard that negation inverts the sign bit for all inputs including NaN.
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) 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 either whose class is
compatible with a specified reference type or list of reference types,
or which embodies a value of a primitive type.
The following production from §4.4 is shown here for convenience:
The parentheses and the type or list of types they contain are sometimes called the cast operator.
If the cast operator contains a list of types, that is, a ReferenceType followed by one or more AdditionalBound terms, then all of the following must be true, or a compile-time error occurs:
The erasures (§4.6) of all the listed types must be pairwise different.
No two listed types may be subtypes of different parameterizations of the same generic interface.
The target type for the casting context (§5.5) introduced by the cast expression is either the PrimitiveType or the ReferenceType (if not followed by AdditionalBound terms) appearing in the cast operator, or the intersection type denoted by the ReferenceType and AdditionalBound terms appearing in the cast operator.
The type of a cast expression is the result of applying capture conversion (§5.1.10) to this target type.
Casts can be used to explicitly "tag" a lambda expression or a method reference expression with a particular target type. To provide an appropriate degree of flexibility, the target type may be a list of types denoting an intersection type, provided the intersection induces a functional interface (§9.8).
The result of a cast expression is not a variable, but a value, even if the result of evaluating the operand expression is a variable.
If the compile-time type of the operand cannot be converted by casting conversion (§5.5) to the target type specified by the cast operator, then a compile-time error occurs.
Otherwise, at run time, the operand value is converted (if necessary) by casting conversion to the target type specified by the cast operator.
A ClassCastException
is thrown if a cast is found at run time to be impermissible.
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.
The operators *
, /
, and %
are called
the multiplicative operators.
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).
Note that binary numeric promotion may include 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.
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 representable value using the round to nearest rounding policy (§15.4). The Java programming language requires support of gradual underflow as defined by IEEE 754.
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) 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.
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 representable value using the round to nearest rounding policy (§15.4). The Java programming language requires support of gradual underflow as defined by IEEE 754.
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) 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
computed by the remainder operation in IEEE 754, due to the choice of
rounding policy in the Java programming language (§15.4). 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 operands to behave in a manner analogous to that of the
integer remainder operator, with an implied division using the round
toward zero rounding policy; this may be compared with the C library
function fmod
. The IEEE 754 remainder operation may
be computed by the library routine
Math.IEEEremainder
or
StrictMath.IEEEremainder
.
The result of a floating-point remainder operation is determined by the following rules, which match IEEE 754 arithmetic except for how the implied division is computed:
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.
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 constant expression (§15.29).
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).
Note that binary numeric promotion may include 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 by the 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.
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 representable value using the round to nearest rounding policy (§15.4). The Java programming language requires support of gradual underflow.
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.
The shift operators are syntactically left-associative (they group left-to-right).
Unary numeric promotion (§5.6) is performed on each operand separately. (Binary numeric promotion 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
, then 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 floor(n /
2s). 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.
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).
Note that binary numeric promotion may include 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.
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
.
An instanceof
expression may perform either type comparison or
pattern matching.
If the operand to the right of the instanceof
keyword is a
ReferenceType, then the instanceof
keyword is the type
comparison operator.
If the operand to the right of the instanceof
keyword is a
Pattern, then the instanceof
keyword is the pattern match
operator.
The following rules apply when instanceof
is the type comparison
operator:
The type of the expression RelationalExpression must be a reference type or the null type, or a compile-time error occurs.
The RelationalExpression must be downcast compatible with the ReferenceType (§5.5), or a compile-time error occurs.
At run time, the result of the type comparison operator is determined as follows:
If the value of the RelationalExpression is the null reference
(§4.1), then the result is false
.
If the value of the RelationalExpression is not the null
reference, then the result is true
if the value could be cast
to the ReferenceType without raising a ClassCastException
, and false
otherwise.
The following rules apply when instanceof
is the pattern match
operator:
The type of the expression RelationalExpression must be a reference type or the null type, or a compile-time error occurs.
The RelationalExpression must be compatible with the Pattern (§14.30.1), or a compile-time error occurs.
If the type of the RelationalExpression is a subtype of the type of the Pattern, then a compile-time error occurs.
At run time, the result of the pattern match operator is determined as follows:
If the value of the RelationalExpression is the null reference,
then the result is false
.
If the value of the RelationalExpression is not the null
reference, then the result is true
if the value matches
the Pattern (§14.30.2), and false
otherwise.
A side effect of a true
result is that
pattern variables declared in Pattern will be initialized.
Example 15.20.2-1. The Type Comparison 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
.
Prior to Java SE 16, the ReferenceType operand of
a type comparison operator was required to be reifiable (§4.7). This prevented the use of a parameterized type
unless all its type arguments were wildcards. The requirement was
lifted in Java SE 16 to allow more parameterized types to be used. For
example, in the following program, it is legal to test whether the
method parameter x
, with static type
List<Integer>
, has a more "refined"
parameterized type ArrayList<Integer>
at run
time:
import java.util.ArrayList; import java.util.List; class Test2 { public static void main(String[] args) { List<Integer> x = new ArrayList<Integer>(); if (x instanceof ArrayList<Integer>) { // OK System.out.println("ArrayList of Integers"); } if (x instanceof ArrayList<String>) { // error System.out.println("ArrayList of Strings"); } if (x instanceof ArrayList<Object>) { // error System.out.println("ArrayList of Objects"); } } }
The first instanceof
expression is legal because
there is a casting conversion from
List<Integer>
to
ArrayList<Integer>
. However, the second and
third instanceof
expressions both cause a compile-time error because
there is no casting conversion from
List<Integer>
to
ArrayList<String>
or
ArrayList<Object>
.
The
operators ==
(equal to) and !=
(not equal to) are called the equality
operators.
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).
Note that binary numeric promotion may include 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.
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 (ignoring the case where both values are
null
).
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,
§3.10.6). 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 |
.
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).
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
.
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
.
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,
||
computes 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.
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.
There are three kinds of conditional expressions, classified according to the second and third operand expressions: boolean conditional expressions, numeric conditional expressions, and reference conditional expressions. The classification rules are as follows:
If both the second and the third operand expressions are boolean expressions, the conditional expression is a boolean conditional expression.
For the purpose of classifying a conditional, the following expressions are boolean expressions:
An expression of a standalone form (§15.2)
that has type boolean
or Boolean
.
A parenthesized boolean
expression
(§15.8.5).
A class instance creation expression
(§15.9) for class Boolean
.
A method invocation expression (§15.12)
for which the chosen most specific method
(§15.12.2.5) has return type boolean
or Boolean
.
Note that, for a generic method, this is the type before instantiating the method's type arguments.
A switch
expression (§15.28)
whose result expressions are all boolean
expressions.
If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.
For the purpose of classifying a conditional, the following expressions are numeric expressions:
An expression of a standalone form (§15.2) with a type that is convertible to a numeric type (§4.2, §5.1.8).
A parenthesized numeric expression (§15.8.5).
A class instance creation expression (§15.9) for a class that is convertible to a numeric type.
A method invocation expression (§15.12) for which the chosen most specific method (§15.12.2.5) has a return type that is convertible to a numeric type.
Note that, for a generic method, this is the type before instantiating the method's type arguments.
A switch
expression (§15.28)
whose result expressions are all numeric expressions.
Otherwise, the conditional expression is a reference conditional expression.
The process for determining the type of a conditional expression depends on the kind of conditional expression, as outlined in the following sections.
The following tables summarize the rules above by giving
the type of a conditional expression for all possible types of its
second and third operands. bnp(..) means to apply binary numeric
promotion. The form "T | bnp(..)" is used where one operand is a
constant expression of type int
and may be representable in type
T, where binary numeric promotion is used if the operand is not
representable in type T. The operand type Object
means any
reference type other than the null
type and the eight wrapper
classes Boolean
, Byte
, Short
, Character
, Integer
, Long
,
Float
, Double
.
Table 15.25-A. Conditional expression type (Primitive 3rd operand, Part I)
3rd → | byte |
short |
char |
int |
---|---|---|---|---|
2nd ↓ | ||||
byte |
byte |
short |
bnp(byte ,char )
|
byte | bnp(byte ,int )
|
Byte |
byte |
short |
bnp(Byte ,char )
|
byte | bnp(Byte ,int )
|
short |
short |
short |
bnp(short ,char )
|
short | bnp(short ,int )
|
Short |
short |
short |
bnp(Short ,char )
|
short | bnp(Short ,int )
|
char |
bnp(char ,byte )
|
bnp(char ,short )
|
char |
char | bnp(char ,int )
|
Character |
bnp(Character ,byte )
|
bnp(Character ,short )
|
char |
char | bnp(Character ,int )
|
int |
byte | bnp(int ,byte )
|
short | bnp(int ,short )
|
char | bnp(int ,char )
|
int |
Integer |
bnp(Integer ,byte )
|
bnp(Integer ,short )
|
bnp(Integer ,char )
|
int |
long |
bnp(long ,byte )
|
bnp(long ,short )
|
bnp(long ,char )
|
bnp(long ,int )
|
Long |
bnp(Long ,byte )
|
bnp(Long ,short )
|
bnp(Long ,char )
|
bnp(Long ,int )
|
float |
bnp(float ,byte )
|
bnp(float ,short )
|
bnp(float ,char )
|
bnp(float ,int )
|
Float |
bnp(Float ,byte )
|
bnp(Float ,short )
|
bnp(Float ,char )
|
bnp(Float ,int )
|
double |
bnp(double ,byte )
|
bnp(double ,short )
|
bnp(double ,char )
|
bnp(double ,int )
|
Double |
bnp(Double ,byte )
|
bnp(Double ,short )
|
bnp(Double ,char )
|
bnp(Double ,int )
|
boolean |
lub(Boolean ,Byte )
|
lub(Boolean ,Short )
|
lub(Boolean ,Character )
|
lub(Boolean ,Integer )
|
Boolean |
lub(Boolean ,Byte )
|
lub(Boolean ,Short )
|
lub(Boolean ,Character )
|
lub(Boolean ,Integer )
|
null |
lub(null ,Byte )
|
lub(null ,Short )
|
lub(null ,Character )
|
lub(null ,Integer )
|
Object |
lub(Object ,Byte )
|
lub(Object ,Short )
|
lub(Object ,Character )
|
lub(Object ,Integer )
|
Table 15.25-B. Conditional expression type (Primitive 3rd operand, Part II)
3rd → | long |
float |
double |
boolean |
---|---|---|---|---|
2nd ↓ | ||||
byte |
bnp(byte ,long )
|
bnp(byte ,float )
|
bnp(byte ,double )
|
lub(Byte ,Boolean )
|
Byte |
bnp(Byte ,long )
|
bnp(Byte ,float )
|
bnp(Byte ,double )
|
lub(Byte ,Boolean )
|
short |
bnp(short ,long )
|
bnp(short ,float )
|
bnp(short ,double )
|
lub(Short ,Boolean )
|
Short |
bnp(Short ,long )
|
bnp(Short ,float )
|
bnp(Short ,double )
|
lub(Short ,Boolean )
|
char |
bnp(char ,long )
|
bnp(char ,float )
|
bnp(char ,double )
|
lub(Character ,Boolean )
|
Character |
bnp(Character ,long )
|
bnp(Character ,float )
|
bnp(Character ,double )
|
lub(Character ,Boolean )
|
int |
bnp(int ,long )
|
bnp(int ,float )
|
bnp(int ,double )
|
lub(Integer ,Boolean )
|
Integer |
bnp(Integer ,long )
|
bnp(Integer ,float )
|
bnp(Integer ,double )
|
lub(Integer ,Boolean )
|
long |
long |
bnp(long ,float )
|
bnp(long ,double )
|
lub(Long ,Boolean )
|
Long |
long |
bnp(Long ,float )
|
bnp(Long ,double )
|
lub(Long ,Boolean )
|
float |
bnp(float ,long )
|
float |
bnp(float ,double )
|
lub(Float ,Boolean )
|
Float |
bnp(Float ,long )
|
float |
bnp(Float ,double )
|
lub(Float ,Boolean )
|
double |
bnp(double ,long )
|
bnp(double ,float )
|
double |
lub(Double ,Boolean )
|
Double |
bnp(Double ,long )
|
bnp(Double ,float )
|
double |
lub(Double ,Boolean )
|
boolean |
lub(Boolean ,Long )
|
lub(Boolean ,Float )
|
lub(Boolean ,Double )
|
boolean |
Boolean |
lub(Boolean ,Long )
|
lub(Boolean ,Float )
|
lub(Boolean ,Double )
|
boolean |
null |
lub(null ,Long )
|
lub(null ,Float )
|
lub(null ,Double )
|
lub(null ,Boolean )
|
Object |
lub(Object ,Long )
|
lub(Object ,Float )
|
lub(Object ,Double )
|
lub(Object ,Boolean )
|
Table 15.25-C. Conditional expression type (Reference 3rd operand, Part I)
3rd → | Byte |
Short |
Character |
Integer |
---|---|---|---|---|
2nd ↓ | ||||
byte |
byte |
short |
bnp(byte ,Character )
|
bnp(byte ,Integer )
|
Byte |
Byte |
short |
bnp(Byte ,Character )
|
bnp(Byte ,Integer )
|
short |
short |
short |
bnp(short ,Character )
|
bnp(short ,Integer )
|
Short |
short |
Short |
bnp(Short ,Character )
|
bnp(Short ,Integer )
|
char |
bnp(char ,Byte )
|
bnp(char ,Short )
|
char |
bnp(char ,Integer )
|
Character |
bnp(Character ,Byte )
|
bnp(Character ,Short )
|
Character |
bnp(Character ,Integer )
|
int |
byte | bnp(int ,Byte )
|
short | bnp(int ,Short )
|
char | bnp(int ,Character )
|
int |
Integer |
bnp(Integer ,Byte )
|
bnp(Integer ,Short )
|
bnp(Integer ,Character )
|
Integer |
long |
bnp(long ,Byte )
|
bnp(long ,Short )
|
bnp(long ,Character )
|
bnp(long ,Integer )
|
Long |
bnp(Long ,Byte )
|
bnp(Long ,Short )
|
bnp(Long ,Character )
|
bnp(Long ,Integer )
|
float |
bnp(float ,Byte )
|
bnp(float ,Short )
|
bnp(float ,Character )
|
bnp(float ,Integer )
|
Float |
bnp(Float ,Byte )
|
bnp(Float ,Short )
|
bnp(Float ,Character )
|
bnp(Float ,Integer )
|
double |
bnp(double ,Byte )
|
bnp(double ,Short )
|
bnp(double ,Character )
|
bnp(double ,Integer )
|
Double |
bnp(Double ,Byte )
|
bnp(Double ,Short )
|
bnp(Double ,Character )
|
bnp(Double ,Integer )
|
boolean |
lub(Boolean ,Byte )
|
lub(Boolean ,Short )
|
lub(Boolean ,Character )
|
lub(Boolean ,Integer )
|
Boolean |
lub(Boolean ,Byte )
|
lub(Boolean ,Short )
|
lub(Boolean ,Character )
|
lub(Boolean ,Integer )
|
null |
Byte |
Short |
Character |
Integer |
Object |
lub(Object ,Byte )
|
lub(Object ,Short )
|
lub(Object ,Character )
|
lub(Object ,Integer )
|
Table 15.25-D. Conditional expression type (Reference 3rd operand, Part II)
3rd → | Long |
Float |
Double |
Boolean |
---|---|---|---|---|
2nd ↓ | ||||
byte |
bnp(byte ,Long )
|
bnp(byte ,Float )
|
bnp(byte ,Double )
|
lub(Byte ,Boolean )
|
Byte |
bnp(Byte ,Long )
|
bnp(Byte ,Float )
|
bnp(Byte ,Double )
|
lub(Byte ,Boolean )
|
short |
bnp(short ,Long )
|
bnp(short ,Float )
|
bnp(short ,Double )
|
lub(Short ,Boolean )
|
Short |
bnp(Short ,Long )
|
bnp(Short ,Float )
|
bnp(Short ,Double )
|
lub(Short ,Boolean )
|
char |
bnp(char ,Long )
|
bnp(char ,Float )
|
bnp(char ,Double )
|
lub(Character ,Boolean )
|
Character |
bnp(Character ,Long )
|
bnp(Character ,Float )
|
bnp(Character ,Double )
|
lub(Character ,Boolean )
|
int |
bnp(int ,Long )
|
bnp(int ,Float )
|
bnp(int ,Double )
|
lub(Integer ,Boolean )
|
Integer |
bnp(Integer ,Long )
|
bnp(Integer ,Float )
|
bnp(Integer ,Double )
|
lub(Integer ,Boolean )
|
long |
long |
bnp(long ,Float )
|
bnp(long ,Double )
|
lub(Long ,Boolean )
|
Long |
Long |
bnp(Long ,Float )
|
bnp(Long ,Double )
|
lub(Long ,Boolean )
|
float |
bnp(float ,Long )
|
float |
bnp(float ,Double )
|
lub(Float ,Boolean )
|
Float |
bnp(Float ,Long )
|
Float |
bnp(Float ,Double )
|
lub(Float ,Boolean )
|
double |
bnp(double ,Long )
|
bnp(double ,Float )
|
double |
lub(Double ,Boolean )
|
Double |
bnp(Double ,Long )
|
bnp(Double ,Float )
|
Double |
lub(Double ,Boolean )
|
boolean |
lub(Boolean ,Long )
|
lub(Boolean ,Float )
|
lub(Boolean ,Double )
|
boolean |
Boolean |
lub(Boolean ,Long )
|
lub(Boolean ,Float )
|
lub(Boolean ,Double )
|
Boolean |
null |
Long |
Float |
Double |
Boolean |
Object |
lub(Object ,Long )
|
lub(Object ,Float )
|
lub(Object ,Double )
|
lub(Object ,Boolean )
|
Table 15.25-E. Conditional expression type (Reference 3rd operand, Part III)
3rd → | null |
Object |
---|---|---|
2nd ↓ | ||
byte |
lub(Byte ,null )
|
lub(Byte ,Object )
|
Byte |
Byte |
lub(Byte ,Object )
|
short |
lub(Short ,null )
|
lub(Short ,Object )
|
Short |
Short |
lub(Short ,Object )
|
char |
lub(Character ,null )
|
lub(Character ,Object )
|
Character |
Character |
lub(Character ,Object )
|
int |
lub(Integer ,null )
|
lub(Integer ,Object )
|
Integer |
Integer |
lub(Integer ,Object )
|
long |
lub(Long ,null )
|
lub(Long ,Object )
|
Long |
Long |
lub(Long ,Object )
|
float |
lub(Float ,null )
|
lub(Float ,Object )
|
Float |
Float |
lub(Float ,Object )
|
double |
lub(Double ,null )
|
lub(Double ,Object )
|
Double |
Double |
lub(Double ,Object )
|
boolean |
lub(Boolean ,null )
|
lub(Boolean ,Object )
|
Boolean |
Boolean |
lub(Boolean ,Object )
|
null |
null |
lub(null ,Object )
|
Object |
Object |
Object |
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 below.
This conversion may include boxing or unboxing conversion (§5.1.7, §5.1.8).
The operand expression not chosen is not evaluated for that particular evaluation of the conditional expression.
Boolean conditional expressions are standalone expressions (§15.2).
The type of a boolean conditional expression is determined as follows:
Numeric conditional expressions are standalone expressions (§15.2).
The type of a numeric conditional expression is determined as follows:
If the second and third operands have the same 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 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.29) 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 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, general numeric promotion (§5.6) is applied to the second and third operands, and the type of the conditional expression is the promoted type of the second and third operands.
Note that numeric promotion may include unboxing conversion (§5.1.8).
A reference conditional expression is a poly expression if it appears in an assignment context or an invocation context (§5.2. §5.3). Otherwise, it is a standalone expression.
Where a poly reference conditional expression appears in a context of a particular kind with target type T, its second and third operand expressions similarly appear in a context of the same kind with target type T.
A poly reference conditional expression is compatible with a target type T if its second and third operand expressions are compatible with T.
The type of a poly reference conditional expression is the same as its target type.
The type of a standalone reference 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 the type of one of the second and third operands is the null type, and the type of the other operand is a reference type, then the type of the conditional expression is that reference type.
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).
Because reference conditional expressions can be poly expressions, they can "pass down" context to their operands. This allows lambda expressions and method reference expressions to appear as operands:
return ... ? (x->
x) : (x->
-x);
It also allows use of extra information to improve type checking of generic method invocations. Prior to Java SE 8, this assignment was well-typed:
List<String> ls = Arrays.asList();
but this was not:
List<String> ls = ... ? Arrays.asList() : Arrays.asList("a","b");
The rules above allow both assignments to be considered well-typed.
Note that a reference conditional expression does
not have to contain a poly expression as an
operand in order to be a poly expression. It is a
poly expression simply by virtue of the context in which it
appears. For example, in the following code, the conditional
expression is a poly expression, and each operand is considered to be
in an assignment context targeting Class<? super
Integer>
:
Class<? super Integer> choose(boolean b, Class<Integer> c1, Class<Number> c2) { return b ? c1 : c2; }
If the conditional expression was not a poly
expression, then a compile-time error would occur, as its type would
be lub(Class<Integer>
,
Class<Number>
) = Class<? extends
Number>
which is incompatible with the return type
of choose
.
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
.
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.10.3).
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 (Definite Assignment))), 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.
If the type of the right-hand operand is not assignment compatible with the type of the variable (§5.2), then a compile-time error occurs.
Otherwise, at run time, the expression is evaluated in one of three ways.
If the left-hand operand expression is a field access expression
e
.
f
(§15.11), 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.10.3), 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, 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, 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, and the result of the conversion is stored into the variable.
If the left-hand operand expression is an array access expression (§15.10.3), 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, 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);
A lambda expression is like a method: it provides a list of formal parameters and a body - an expression or block - expressed in terms of those parameters.
Lambda expressions are always poly expressions (§15.2).
It is a compile-time error if a lambda expression occurs in a program in someplace other than an assignment context (§5.2), an invocation context (§5.3), or a casting context (§5.5).
Evaluation of a lambda expression produces an instance of a functional interface (§9.8). Lambda expression evaluation does not cause the execution of the expression's body; instead, this may occur at a later time when an appropriate method of the functional interface is invoked.
Here are some examples of lambda expressions:
() -> {} // No parameters; result is void () -> 42 // No parameters, expression body () -> null // No parameters, expression body () -> { return 42; } // No parameters, block body with return () -> { System.gc(); } // No parameters, void block body () -> { // Complex block body with returns if (true) return 12; else { int result = 15; for (int i = 1; i < 10; i++) result *= i; return result; } } (int x) -> x+1 // Single declared-type parameter (int x) -> { return x+1; } // Single declared-type parameter (x) -> x+1 // Single inferred-type parameter x -> x+1 // Parentheses optional for // single inferred-type parameter (String s) -> s.length() // Single declared-type parameter (Thread t) -> { t.start(); } // Single declared-type parameter s -> s.length() // Single inferred-type parameter t -> { t.start(); } // Single inferred-type parameter (int x, int y) -> x+y // Multiple declared-type parameters (x, y) -> x+y // Multiple inferred-type parameters (x, int y) -> x+y // Illegal: can't mix inferred and declared types (x, final y) -> x+y // Illegal: no modifiers with inferred types
This syntax has the advantage of minimizing bracket
noise around simple lambda expressions, which is especially beneficial
when a lambda expression is an argument to a method, or when the body
is another lambda expression. It also clearly distinguishes between
its expression and statement forms, which avoids ambiguities or
over-reliance on ';
' tokens. When some extra bracketing is needed
to visually distinguish either the full lambda expression or its body
expression, parentheses are naturally supported (just as in other
cases in which operator precedence is unclear).
The syntax has some parsing challenges. The Java programming language has
always required arbitrary lookahead to
distinguish between types and expressions after a '(
' token:
what follows may be a cast or a parenthesized expression. This was
made worse when generics reused the binary operators '<
' and
'>
' in types. Lambda expressions introduce a new possibility:
the tokens following '(
' may describe a type, an expression, or
a lambda parameter list. Some tokens immediately indicate a parameter
list (annotations, final
); in other cases there are certain patterns
that must be interpreted as parameter lists (two names in a row, a
',
' not nested inside of '<
' and '>
'); and
sometimes, the decision cannot be
made until a '->
' is encountered after a ')
'. The simplest
way to think of how this might be efficiently parsed is with a state
machine: each state represents a subset of possible interpretations
(type, expression, or parameters), and when the machine transitions to
a state in which the set is a singleton, the parser knows which case
it is. This does not map very elegantly to a fixed-lookahead grammar,
however.
There is no special nullary form: a lambda
expression with zero arguments is expressed as ()
. The obvious special-case syntax, ->
...
, does not work because it introduces an ambiguity
between argument lists and casts: ->
...(x)
.
->
...
Lambda expressions cannot declare type parameters. While it would make sense semantically to do so, the natural syntax (preceding the parameter list with a type parameter list) introduces messy ambiguities. For example, consider:
foo( (x) < y , z > (w) ->
v )
This could be an invocation
of foo
with one argument (a generic lambda cast to
type x
), or it could be an invocation
of foo
with two arguments, both the results of
comparisons, the second comparing z
with a lambda
expression. (Strictly speaking, a lambda expression is meaningless as
an operand to the relational operator >
, but that is a
tenuous assumption on which to build the grammar.)
There is a precedent for ambiguity resolution
involving casts, which essentially prohibits the use of -
and
+
following a non-primitive cast (§15.15),
but to extend that approach to generic lambdas would involve invasive
changes to the grammar.
The formal parameters of a lambda expression, if any, are specified by
either a parenthesized list of comma-separated parameter specifiers or
a parenthesized list of comma-separated identifiers. In a list of
parameter specifiers, each parameter specifier consists of optional
modifiers, then a type (or var
), then an identifier that specifies
the name of the parameter. In a list of identifiers, each identifier
specifies the name of the parameter.
If a lambda expression has no formal parameters, then an empty pair of
parentheses appears before the ->
and the lambda body.
If a lambda expression has exactly one formal parameter, and the parameter is specified by an identifier instead of a parameter specifier, then the parentheses around the identifier may be elided.
The following productions from §8.4.1, §8.3, and §4.3 are shown here for convenience:
A formal parameter of a lambda expression may be declared final
, or
annotated, only if specified by a parameter specifier. If a formal
parameter is specified by an identifier instead, then the formal
parameter is not final
and has no annotations.
A formal parameter of a lambda expression may be a variable arity parameter, indicated by an ellipsis following the type in a parameter specifier. At most one variable arity parameter is permitted for a lambda expression. It is a compile-time error if a variable arity parameter appears anywhere in the list of parameter specifiers except the last position.
Each formal parameter of a lambda expression has either an inferred type or a declared type:
If a formal parameter is specified either by a parameter
specifier that uses var
, or by an identifier instead of a
parameter specifier, then the formal parameter has an inferred
type. The type is inferred from the functional interface type
targeted by the lambda expression (§15.27.3).
If a formal parameter is specified by a parameter specifier that does
not use var
, then the formal parameter has a declared type. The
declared type is determined as follows:
If the formal parameter is not a variable arity parameter, then the declared type is denoted by UnannType if no bracket pairs appear in UnannType and VariableDeclaratorId, and specified by §10.2 otherwise.
If the formal parameter is a variable arity parameter, then the declared type is an array type specified by §10.2.
No distinction is made between the following lambda parameter lists:
(int... x)->
BODY (int[] x)->
BODY
Either can be used, whether the functional
interface's abstract
method is fixed arity or variable arity. (This is
consistent with the rules for method overriding.) Since lambda
expressions are never directly invoked, using int...
for the formal parameter where the functional interface
uses int[]
can have no impact on the surrounding
program. In a lambda body, a variable arity parameter is treated just
like an array-typed parameter.
A lambda expression where all the formal parameters have declared types is said to be explicitly typed. A lambda expression where all the formal parameters have inferred types is said to be implicitly typed. A lambda expression with no formal parameters is explicitly typed.
If a lambda expression is implicitly typed, then its lambda body is interpreted according to the context in which it appears. Specifically, the types of expressions in the body, and the checked exceptions thrown by the body, and the type correctness of code in the body all depend on the types inferred for the formal parameters. This implies that inference of formal parameter types must occur "before" attempting to type-check the lambda body.
It is a compile-time error if a lambda expression declares a formal parameter with a declared type and a formal parameter with an inferred type.
This rule prevents a mix of inferred and declared types in the formal
parameters, such as (x, int y) -> BODY
or (var x, int y) -> BODY
. Note that if all the
formal parameters have inferred types, the grammar prevents a mix of
identifiers and var
parameter specifiers, such as (x, var
y) -> BODY
or (var x, y) -> BODY
.
The rules concerning annotation modifiers for a formal parameter declaration are specified in §9.7.4 and §9.7.5.
It is a compile-time error if final
appears more than once as a
modifier for a formal parameter declaration.
It is a compile-time error if the LambdaParameterType
of a formal parameter is var
and the VariableDeclaratorId
of the same formal parameter has one or more bracket pairs.
The scope and shadowing of a formal parameter is specified in §6.3 and §6.4.
References to a formal parameter from a nested class or interface, or a nested lambda expression, are restricted, as specified in §6.5.6.1.
It is a compile-time error for a lambda expression to declare two formal parameters with the same name. (That is, their declarations mention the same Identifier.)
In Java SE 8, the use of _
as the name of a lambda
parameter was forbidden, and its use discouraged as the name for other
kinds of variable (§4.12.3). As of Java SE 9,
_
is a keyword (§3.9) so it cannot be
used as a variable name in any context.
It is a compile-time error if a formal parameter that is declared
final
is assigned to within the body of the lambda expression.
When the lambda expression is invoked (via a method invocation expression (§15.12)), the values of the actual argument expressions initialize newly created parameter variables, each of the declared or inferred type, before execution of the lambda body. The Identifier that appears in the LambdaParameter or directly in the LambdaParameterList or LambdaParameters may be used as a simple name in the lambda body to refer to the formal parameter.
A lambda body is either a single expression or a block (§14.2). Like a method body, a lambda body describes code that will be executed whenever an invocation occurs.
Unlike code appearing in anonymous class declarations, the meaning of
names and the this
and super
keywords appearing in a lambda body,
along with the accessibility of referenced declarations, are the same as
in the surrounding context (except that lambda parameters introduce new
names).
The transparency of this
(both explicit and
implicit) in the body of a lambda expression - that is, treating it
the same as in the surrounding context - allows more flexibility for
implementations, and prevents the meaning of unqualified names in the
body from being dependent on overload resolution.
Practically speaking, it is unusual for a lambda
expression to need to talk about itself (either to call itself
recursively or to invoke its other methods), while it is more common
to want to use names to refer to things in the enclosing class that
would otherwise be shadowed (this
, toString()
).
If it is necessary for a lambda expression to refer to itself (as if
via this
), a method reference or an anonymous inner class should be
used instead.
A block lambda body is void-compatible if every
return statement in the block has the form return
;
.
A block lambda body is value-compatible if it
cannot complete normally (§14.22) and every
return statement in the block has the form return
Expression;
.
It is a compile-time error if a block lambda body is neither void-compatible nor value-compatible.
In a value-compatible block lambda body, the result
expressions are any expressions that may produce an
invocation's value. Specifically, for each statement of the form
return
Expression ;
contained by the body, the Expression
is a result expression.
The following lambda bodies are void-compatible:
()->
{} ()->
{ System.out.println("done"); }
These are value-compatible:
()->
{ return "done"; } ()->
{ if (...) return 1; else return 0; }
These are both:
()->
{ throw new RuntimeException(); } ()->
{ while (true); }
This is neither:
() ->
{ if (...) return "done"; System.out.println("done"); }
The handling of void/value-compatible and the meaning of names in the body jointly serve to minimize the dependency on a particular target type in the given context, which is useful both for implementations and for programmer comprehension. While expressions can be assigned different types during overload resolution depending on the target type, the meaning of unqualified names and the basic structure of the lambda body do not change.
Note that the void/value-compatible definition is not a strictly structural property: "can complete normally" depends on the values of constant expressions, and these may include names that reference constant variables.
Any local variable, formal parameter, or exception parameter used but
not declared in a lambda expression must either be final
or effectively
final (§4.12.4), as specified in
§6.5.6.1.
Any local variable used but not declared in a lambda body must be definitely assigned (§16 (Definite Assignment)) before the lambda body, or a compile-time error occurs.
Similar rules on variable use apply in the body of
an inner class (§8.1.3). The restriction to
effectively final variables prohibits access to dynamically-changing
local variables, whose capture would likely introduce concurrency
problems. Compared to the final
restriction, it reduces the
clerical burden on programmers.
The restriction to effectively final variables
includes standard loop variables, but not enhanced-for
loop
variables, which are treated as distinct for each iteration of the
loop (§14.14.2).
The following lambda bodies demonstrate use of effectively final variables.
void m1(int x) { int y = 1; foo(() -> x+y); // Legal: x and y are both effectively final. } void m2(int x) { int y; y = 1; foo(() -> x+y); // Legal: x and y are both effectively final. } void m3(int x) { int y; if (...) y = 1; foo(() -> x+y); // Illegal: y is effectively final, but not definitely assigned. } void m4(int x) { int y; if (...) y = 1; else y = 2; foo(() -> x+y); // Legal: x and y are both effectively final. }
void m5(int x) { int y; if (...) y = 1; y = 2; foo(() -> x+y); // Illegal: y is not effectively final. } void m6(int x) { foo(() -> x+1); x++; // Illegal: x is not effectively final. } void m7(int x) { foo(() -> x=1); // Illegal: x is not effectively final. } void m8() { int y; foo(() -> y=1); // Illegal: y is not definitely assigned before the lambda. } void m9(String[] arr) { for (String s : arr) { foo(() -> s); // Legal: s is effectively final // (it is a new variable on each iteration) } } void m10(String[] arr) { for (int i = 0; i < arr.length; i++) { foo(() -> arr[i]); // Illegal: i is not effectively final // (it is not final, and is incremented) } }
A lambda expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
The ground target type is derived from T as follows:
If T is a wildcard-parameterized functional interface type and the lambda expression is explicitly typed, then the ground target type is inferred as described in §18.5.3.
If T is a wildcard-parameterized functional interface type and the lambda expression is implicitly typed, then the ground target type is the non-wildcard parameterization (§9.9) of T.
A lambda expression is congruent with a function type if all of the following are true:
The number of lambda parameters is the same as the number of parameter types of the function type.
If the lambda expression is explicitly typed, its formal parameter types are the same as the parameter types of the function type.
If the lambda parameters are assumed to have the same types as the function type's parameter types, then:
If the function type's result is void
, the lambda body is
either a statement expression (§14.8)
or a void
-compatible block.
If the function type's result is a (non-void
) type R,
then either (i) the lambda body is an expression that is
compatible with R in an assignment context, or (ii) the
lambda body is a value-compatible block, and each result
expression (§15.27.2) is compatible
with R in an assignment context.
If a lambda expression is compatible with a target type T, then the type of the expression, U, is the ground target type derived from T.
It is a compile-time error if any class or interface mentioned by either U or the function type of U is not accessible (§6.6) from the class or interface in which the lambda expression appears.
For each non-static
member method m
of U, if the function type
of U has a subsignature of the signature of m
, then a notional
method whose method type is the function type of U is deemed to
override m
, and any compile-time error or unchecked warning
specified in §8.4.8.3 may occur.
A checked exception that can be thrown in the body of the lambda expression may cause a compile-time error, as specified in §11.2.3.
The parameter types of explicitly typed lambdas are required to exactly match those of the function type. While it would be possible to be more flexible - allow boxing or contravariance, for example - this kind of generality seems unnecessary, and is inconsistent with the way overriding works in class declarations. A programmer ought to know exactly what function type is being targeted when writing a lambda expression, so the programmer should thus know exactly what signature must be overridden. (This is not the case for method references, so more flexibility is allowed when they are used.) In addition, more flexibility with parameter types would add to the complexity of type inference and overload resolution.
Note that while boxing is not allowed in a strict invocation context, boxing of lambda result expressions is always allowed - that is, the result expression appears in an assignment context, regardless of the context enclosing the lambda expression. However, if an explicitly typed lambda expression is an argument to an overloaded method, a method signature that avoids boxing or unboxing the lambda result is preferred by the most specific check (§15.12.2.5).
If the body of a lambda is a statement expression
(that is, an expression that would be allowed to stand alone as a
statement), it is compatible with a void
-producing function type;
any result is simply discarded. So, for example, both of the following
are legal:
// Predicate has aboolean
result java.util.function.Predicate<String> p = s->
list.add(s); // Consumer has avoid
result java.util.function.Consumer<String> c = s->
list.add(s);
Generally speaking, a lambda of the form
(
)
->
expr,
where expr is a statement expression, is
interpreted as either (
)
->
{
return
expr;
}
or (
)
->
{
expr;
}
, depending
on the target type.
At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a lambda expression is distinct from execution of the lambda body.
Either
a new instance of a class with the properties below is allocated and
initialized, or an existing instance of a class with the properties
below is referenced. If a new instance is to be created, but there is
insufficient space to allocate the object, evaluation of the lambda
expression completes abruptly by throwing an OutOfMemoryError
.
This implies that the identity of the result of evaluating a lambda
expression (or, of serializing and deserializing a lambda expression)
is unpredictable, and therefore identity-sensitive operations (such as
reference equality (§15.21.3), object locking
(§14.19), and the
System.identityHashCode
method) may produce
different results in different implementations of the Java programming language, or
even upon different lambda expression evaluations in the same
implementation.
The value of a lambda expression is a reference to an instance of a class with the following properties:
The class implements the targeted functional interface type and, if the target type is an intersection type, every other interface type mentioned in the intersection.
Where the lambda expression has type U, for each non-static
member method m
of U:
If the function type of U has a subsignature of the signature
of m
, then the class declares a method that overrides m
. The
method's body has the effect of evaluating the lambda body, if
it is an expression, or of executing the lambda body, if it is a
block; if a result is expected, it is returned from the
method.
If the erasure of the type of a method being overridden differs
in its signature from the erasure of the function type of U,
then before evaluating or executing the lambda body, the
method's body checks that each argument value is an instance of
a subclass or subinterface of the erasure of the corresponding
parameter type in the function type of U; if not, a ClassCastException
is
thrown.
The class overrides no other methods of the targeted functional
interface type or other interface types mentioned above,
although it may override methods of the Object
class.
These rules are meant to offer flexibility to implementations of the Java programming language, in that:
A new object need not be allocated on every evaluation.
Objects produced by different lambda expressions need not belong to different classes (if the bodies are identical, for example).
Every object produced by evaluation need not belong to the same class (captured local variables might be inlined, for example).
If an "existing instance" is available, it need not have been created at a previous lambda evaluation (it might have been allocated during the enclosing class's initialization, for example).
If the targeted functional interface type is a
subtype of java.io.Serializable
, the resulting object will automatically be
an instance of a serializable class. Making an object derived from a
lambda expression serializable can have extra run time overhead and
security implications, so lambda-derived objects are not required to
be serializable "by default".
A switch
expression transfers control to one of several statements
or expressions, depending on the value of an expression; all possible
values of that expression must be handled, and all of the several
statements and expressions must produce a value for the result of the
switch
expression.
The Expression is called the selector expression.
The type of the selector expression must be char
, byte
, short
, int
,
Character
, Byte
, Short
, Integer
, String
, or an enum type
(§8.9), or a compile-time error occurs.
The body of both a switch
expression and a
switch
statement (§14.11) is called a
switch block. General rules which apply to all
switch blocks, whether they appear in switch
expressions or switch
statements, are given in §14.11.1. The following
productions from §14.11.1 are shown here for
convenience:
In addition to the general rules for switch blocks
(§14.11.1), there are further rules for
switch blocks in switch
expressions. Namely, all of the following
must be true for the switch block of a switch
expression,
or a compile-time error occurs:
If the type of the selector expression is not an enum type,
then there is exactly one default
label associated with
the switch block.
If the type of the selector expression is an enum type,
then (i) the set of the case
constants associated with the
switch block includes every enum constant of the enum type,
and (ii) at most one default
label is associated with the
switch block.
A default
label is permitted, but not required,
when the case
labels cover all the enum constants.
If the switch block consists of switch rules, then any switch rule block cannot complete normally (§14.22).
If the switch block consists of switch labeled statement groups, then the last statement in the switch block cannot complete normally, and the switch block does not have any switch labels after the last switch labeled statement group.
switch
expressions cannot have empty switch blocks,
unlike switch
statements. Furthermore, switch
expressions differ from
switch
statements in terms of which expressions may appear to the right
of an arrow (->
) in the switch block, that is, which expressions may
be used as switch rule expressions. In a switch
expression, any expression may be used as a switch rule expression, but
in a switch
statement, only a statement expression may be used
(§14.11.1).
The result expressions of a switch
expression
are determined as follows:
If the switch block consists of switch rules, then each switch rule is considered in turn:
If the switch rule is of the form
...
->
Expression ;
then Expression is a result expression of the
switch
expression.
If the switch rule is of the form ...
->
Block
then every expression which is immediately contained in a
yield
statement in Block whose yield target is the
given switch
expression, is a result expression
of the switch
expression.
If the switch block consists of switch labeled statement groups,
then every expression immediately contained in a yield
statement
in the switch block whose yield target is the given switch
expression,
is a result expression of the switch
expression.
It is a compile-time error if a switch
expression has no result expressions.
A switch
expression is a poly expression if it appears in an
assignment context or an invocation context (§5.2,
§5.3). Otherwise, it is a standalone expression.
Where a poly switch
expression appears in a context of a particular kind
with target type T, its result expressions similarly appear in a
context of the same kind with target type T.
A poly switch
expression is compatible with a target type T if each
of its result expressions is compatible with T.
The type of a poly switch
expression is the same as its target type.
The type of a standalone switch
expression is determined as follows:
If the result expressions all have the same type
(which may be the null type (§4.1)),
then that is the type of the switch
expression.
Otherwise, if the type of each result expression is boolean
or
Boolean
, then an unboxing conversion (§5.1.8)
is applied to each result expression of type Boolean
,
and the switch
expression has type boolean
.
Otherwise, if the type of each result expression is
convertible to a numeric type (§5.1.8),
then the type of the switch
expression is the result of
general numeric promotion (§5.6)
applied to the result expressions.
Otherwise, boxing conversion (§5.1.7) is
applied to each result expression that has a primitive type,
after which the type of the switch
expression is the result
of applying capture conversion (§5.1.10)
to the least upper bound (§4.10.4)
of the types of the result expressions.
A switch
expression is evaluated by first evaluating the
selector expression. Then:
If evaluation of the selector expression completes abruptly,
then the switch
expression completes abruptly for the
same reason.
Otherwise, if the result of evaluating the selector expression is
null
, then a NullPointerException
is thrown and the entire switch
expression
completes abruptly for that reason.
Otherwise, if the result of evaluating the selector expression
is of type Character
, Byte
, Short
, or Integer
,
it is subjected to unboxing conversion (§5.1.8).
If this conversion completes abruptly, then the entire switch
expression completes abruptly for the same reason.
If evaluation of the selector expression completes normally and the
result is non-null
, and the subsequent unboxing conversion (if any)
completes normally, then evaluation of the switch
expression continues
by determining if a switch label associated with the switch block matches
the value of the selector expression (§14.11.1).
Then:
If no switch label matches, then an IncompatibleClassChangeError
is thrown and the
entire switch
expression completes abruptly for that reason.
If a switch label matches, then one of the following applies:
If it is the switch label for a switch rule expression, then
the expression is evaluated. If the result of evaluation is
a value, then the switch
expression completes normally
with the same value.
If it is the switch label for a switch rule block,
then the block is executed. If this block completes normally,
then the switch
expression completes normally.
If it is the switch label for a switch rule throw
statement,
then the throw
statement is executed.
Otherwise, all the statements in the switch block after the
matching switch label are executed in order. If these statements
complete normally, then the switch
expression completes
normally.
If execution of any statement or expression in the switch block completes abruptly, it is handled as follows:
If execution of an expression completes abruptly, then the
switch
expression completes abruptly for the same reason.
If execution of a statement completes abruptly because of a
yield
with value V, then the switch
expression completes
normally and the value of the switch
expression is V.
If execution of a statement completes abruptly for any reason
other than a yield
with a value, then the switch
expression
completes abruptly for the same reason.
A 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 (§3.10.1, §3.10.2, §3.10.3, §3.10.4), string literals (§3.10.5), and text blocks (§3.10.6)
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).
Constant expressions of type String
are always "interned" so as to
share unique instances, using the method
String.intern
.
Constant expressions are used as case
labels in switch
statements
and switch
expressions (§14.11, §15.28) and have a special significance in assignment
contexts (§5.2) and the 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.22), and the type of a conditional operator
? :
with numeric operands.
Example 15.29-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."