former takes an argument of type String
while the latter takes
an
argument of type Object
. The invocation fails with a ClassCastException
before
the body of the method is executed.
Such situations can only arise if the program gives rise to a compile-time unchecked warning (§4.8, §5.1.9, §5.5.2, §8.4.1, §<IP_ADDRESS>, §<IP_ADDRESS>, §<IP_ADDRESS>, §<IP_ADDRESS>).
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 T be the type declaration
immediately enclosing the method reference expression. It is a
compile-time error if T is the class Object
or T 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 T be the type declaration immediately enclosing the method reference expression. It is a compile-time error if I is not a direct superinterface of T, or if there exists some other direct superclass or direct superinterface of T, J, such that J is a subtype 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.
If a method
reference expression has the form ClassType ::
[TypeArguments] new
, then:
ClassType must denote a class that is accessible,
non-abstract
, and not an enum type, 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
(§<IP_ADDRESS>) 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 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). Method reference evaluation does not cause the execution of the corresponding method; instead, this 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 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).
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 class whose declaration
contains the method reference.
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, and the
corresponding
superinterface type of the class or interface whose
declaration contains the method reference is the type to
search.
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, the potentially applicable
methods are the
member methods of the type to search that have an
appropriate name (given by Identifier),
accessibility,
arity (n or n-1),
and type
argument arity (derived from [TypeArguments]), as
specified in §<IP_ADDRESS>.
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
, 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 methods with appropriate accessibility, arity (n), and type argument arity (derived from [TypeArguments]) are selected, as specified in §<IP_ADDRESS>.
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 have an appropriate name (given by Identifier), accessibility, arity (n), and type argument arity (derived from [TypeArguments]), as specified in §<IP_ADDRESS>.
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 §<IP_ADDRESS>
through
§<IP_ADDRESS>,
with the clarifications
below. Each search may produce a method or, in the case of
an error as specified in §<IP_ADDRESS>
through §<IP_ADDRESS>,
no result.
In the first search, 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.
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. Again, the type
arguments, if any, are given by the method reference
expression.
If the first search produces a static
method, and
no
non-static
method is applicable by
§<IP_ADDRESS>,
§<IP_ADDRESS>,
or
§<IP_ADDRESS>
during the second search,
then the compile-time declaration is the result of the first
search.
Otherwise, if no static
method is applicable by
§<IP_ADDRESS>,
§<IP_ADDRESS>,
or
§<IP_ADDRESS>
during the first search, and
the second search produces a non-static
method,
then the
compile-time declaration is the result 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 §<IP_ADDRESS> through §<IP_ADDRESS>, 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
§<IP_ADDRESS>
through
§<IP_ADDRESS>,
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 TypeName .
super
::
[TypeArguments]
Identifier, and TypeName
denotes an interface, and there exists a
method, distinct from the compile-time declaration, that overrides
(§8.4.8,
§9.4.1) the
compile-time declaration from a direct superclass or direct
superinterface of the type whose declaration immediately encloses the
method reference expression.
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 (§<IP_ADDRESS>), 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
(§<IP_ADDRESS>) 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.
A compile-time unchecked warning occurs 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.
A compile-time unchecked warning occurs 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.
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 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 §<IP_ADDRESS> 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
§<IP_ADDRESS>,
§<IP_ADDRESS>, and
§<IP_ADDRESS>,
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
§<IP_ADDRESS>,
§<IP_ADDRESS>, and
§<IP_ADDRESS>,
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
§<IP_ADDRESS>,
§<IP_ADDRESS>, and
§<IP_ADDRESS>,
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 effect of this determination when the compile-time declaration of the method reference is signature polymorphic is that:
The types of the parameters for the method invocation are the types of the corresponding arguments.
The method invocation is either void
or has a
return type of Object
, depending on whether the invocation
method which encloses the method invocation is void
or has a
return type.
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.2) is performed on the
value 1
and the value of the variable. If
necessary, the sum is narrowed by a narrowing primitive conversion
(§5.1.3)
and/or subjected to boxing conversion
(§5.1.7) to
the type of the variable before it is
stored. The value of the postfix increment expression is the value of
the variable before the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the sum prior to its being stored in the variable.
A variable
that is declared final
cannot be incremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
postfix increment operator.
A postfix expression
followed by a --
operator is a postfix decrement
expression.
The result of the postfix expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.
The type of the postfix decrement expression is the type of the variable. The result of the postfix decrement expression is not a variable, but a value.
At run
time, if evaluation of the operand expression completes abruptly, then
the postfix decrement expression completes abruptly for the same
reason and no decrementation occurs. Otherwise, the
value 1
is subtracted from the value of the
variable and the difference is stored back into the variable. Before
the subtraction, binary numeric promotion
(§5.6.2) is performed on the
value 1
and the value of the variable. If
necessary, the difference is narrowed by a narrowing primitive
conversion (§5.1.3) and/or subjected to
boxing
conversion (§5.1.7)
to the type of the variable
before it is stored. The value of the postfix decrement expression is
the value of the variable before the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the difference prior to its being stored in the variable.
A variable
that is declared final
cannot be decremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
postfix decrement operator.
The operators +
,
-
, ++
, --
, ~
, !
, and the cast
operator (§15.16)
are called the unary
operators.
The following production from §15.16 is shown here for convenience:
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
shown above splits CastExpression into two 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
--
.

The result of the unary expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.
The type of the prefix increment expression is the type of the variable. The result of the prefix increment expression is not a variable, but a value.
At run
time, if evaluation of the operand expression completes abruptly, then
the prefix increment expression completes abruptly for the same reason
and no incrementation occurs. Otherwise, the
value 1
is added to the value of the variable and
the sum is stored back into the variable. Before the addition, binary
numeric promotion (§5.6.2) is performed on the
value 1
and the value of the variable. If
necessary, the sum is narrowed by a narrowing primitive conversion
(§5.1.3)
and/or subjected to boxing conversion
(§5.1.7) to
the type of the variable before it is
stored. The value of the prefix increment expression is the value of
the variable after the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the sum prior to its being stored in the variable.
A variable
that is declared final
cannot be incremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
prefix increment operator.

The result of the unary expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.
The type of the prefix decrement expression is the type of the variable. The result of the prefix decrement expression is not a variable, but a value.
At run time, if evaluation
of the operand expression completes abruptly, then the prefix
decrement expression completes abruptly for the same reason and no
decrementation occurs. Otherwise, the value 1
is
subtracted from the value of the variable and the difference is stored
back into the variable. Before the subtraction, binary numeric
promotion (§5.6.2)
is performed on the
value 1
and the value of the variable. If
necessary, the difference is narrowed by a narrowing primitive
conversion (§5.1.3) and/or subjected to
boxing
conversion (§5.1.7)
to the type of the variable
before it is stored. The value of the prefix decrement expression is
the value of the variable after the new value is
stored.
Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, format conversion is applied to the difference prior to its being stored in the variable.
A variable
that is declared final
cannot be decremented because when an access of
such a final
variable is used as an expression, the result is a
value, not a variable. Thus, it cannot be used as the operand of a
prefix decrement operator.
The type
of the operand expression of the unary +
operator
must be a type that is convertible (§5.1.8) to a
primitive numeric type, or a compile-time error occurs.
Unary numeric promotion (§5.6.1) is performed on the operand. The type of the unary plus expression is the promoted type of the operand. The result of the unary plus expression is not a variable, but a value, even if the result of the operand expression is a variable.
At run time, the value of the unary plus expression is the promoted value of the operand.
The type
of the operand expression of the unary -
operator
must be a type that is convertible (§5.1.8) to a
primitive numeric type, or a compile-time error occurs.
Unary numeric promotion (§5.6.1) is performed on the operand.
The type of the unary minus expression is the promoted type of the operand.
Note that unary numeric promotion performs value set conversion (§5.1.13). Whatever value set the promoted operand value is drawn from, the unary negation operation is carried out and the result is drawn from that same value set. That result is then subject to further value set conversion.
At run time, the value of the unary minus expression is the arithmetic negation of the promoted value of the operand.
For
integer values, negation is the same as subtraction from zero. The
Java programming language uses two's-complement representation for integers, and the
range of two's-complement values is not symmetric, so negation of the
maximum negative int
or long
results in that
same maximum negative
number. Overflow occurs in this case, but no exception is thrown. For
all integer values x
, -x
equals (~x)+1
.
For
floating-point values, negation is not the same
as subtraction from zero, because if x
is +0.0
, then 0.0-x
is +0.0
, but -x
is -0.0
. Unary minus merely inverts the sign of a
floating-point number. Special cases of interest:
If the operand is NaN, the result is NaN. (Recall that NaN has no sign (§4.2.3).)
If the operand is an infinity, the result is the infinity of opposite sign.
If the operand is a zero, the result is the zero of opposite sign.
The type
of the operand expression of the unary ~
operator
must be a type that is convertible (§5.1.8) to a
primitive integral type, or a compile-time error occurs.
Unary numeric promotion (§5.6.1) is performed on the operand. The type of the unary bitwise complement expression is the promoted type of the operand.
At run
time, the value of the unary bitwise complement expression is the
bitwise complement of the promoted value of the operand. In all
cases, ~x
equals (-x)-1
.
The type
of the operand expression of the unary !
operator
must be boolean
or Boolean
, or a compile-time
error occurs.
The type
of the unary logical complement expression is boolean
.
At run
time, the operand is subject to unboxing conversion
(§5.1.8) if
necessary. The value of the unary
logical complement expression is true
if the (possibly converted)
operand value is false
, and false
if the
(possibly converted)
operand value is true
.
A cast
expression converts, at run time, a value of one numeric type to a
similar value of another numeric type; or confirms, at compile time,
that the type of an expression is boolean
; or checks, at run time,
that a reference value refers to an object whose class is compatible
with a specified reference type or list of reference
types.
The parentheses and the type or list of types they contain are sometimes called the cast operator.
The following production from §4.4 is shown here for convenience:
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 the operand expression is a variable.
A cast
operator has no effect on the choice of value set
(§4.2.3)
for a value of type float
or type
double
. Consequently, a cast to type float
within
an expression
that is not FP-strict (§15.4)
does not
necessarily cause its value to be converted to an element of the float
value set, and a cast to type double
within an expression that is
not FP-strict does not necessarily cause its value to be converted to
an element of the double value set.
It is a compile-time error if the compile-time type of the operand may never be cast to the type specified by the cast operator according to the rules of casting conversion (§5.5).
Otherwise, at run time, the operand value is converted (if necessary) by casting conversion to the type specified by the cast operator.
A ClassCastException
is thrown if a cast is found at run time to be impermissible.
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.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
The type of a multiplicative expression is the promoted type of its operands.
If the
promoted type is int
or long
, then integer
arithmetic is
performed.
If the
promoted type is float
or double
, then
floating-point arithmetic
is performed.
The
binary *
operator performs multiplication,
producing the product of its operands.
Multiplication is a commutative operation if the operand expressions have no side effects.
Integer multiplication is associative when the operands are all of the same type.
Floating-point multiplication is not associative.
If an integer multiplication overflows, then the result is the low-order bits of the mathematical product as represented in some sufficiently large two's-complement format. As a result, if overflow occurs, then the sign of the result may not be the same as the sign of the mathematical product of the two operand values.
The result of a floating-point multiplication is determined by the rules of IEEE 754 arithmetic:
If the result is not NaN, the sign of the result is positive if both operands have the same sign, and negative if the operands have different signs.
Multiplication of an infinity by a finite value results in a signed infinity. The sign is determined by the rule stated above.
In the remaining cases, where neither an infinity nor NaN is involved, the exact mathematical product is computed. A floating-point value set is then chosen:
If the multiplication expression is FP-strict (§15.4):
If the multiplication expression is not FP-strict:
If the type
of the
multiplication expression is float
,
then either the
float value set or the float-extended-exponent value set
may be chosen, at the whim of the implementation.
If the type
of the
multiplication expression is double
,
then either the
double value set or the double-extended-exponent value set
may be chosen, at the whim of the implementation.
Next, a value must be chosen from the chosen value set to represent the product.
If the magnitude of the product is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign.
Otherwise, the product is rounded to the nearest value in the chosen value set using IEEE 754 round-to-nearest mode. The Java programming language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
Despite
the fact that overflow, underflow, or loss of information may occur,
evaluation of a multiplication operator *
never throws a
run-time exception.
The
binary /
operator performs division, producing the
quotient of its operands. The left-hand operand is
the dividend and the right-hand operand is
the divisor.
Integer
division rounds toward 0
. That is, the quotient
produced for operands n and d that are integers after binary
numeric promotion (§5.6.2) is an integer value
q whose magnitude is as large as possible while satisfying
|d ⋅ q| ≤ |n|.
Moreover, q is positive
when |n| ≥ |d| and n and d have the same sign,
but q is negative when |n|
≥ |d| and n and
d have opposite signs.
There
is one special case that does not satisfy this rule: if the dividend
is the negative integer of largest possible magnitude for its type,
and the divisor is -1
, then integer overflow occurs
and the result is equal to the dividend. Despite the overflow, no
exception is thrown in this case. On the other hand, if the value of
the divisor in an integer division is 0
, then an
ArithmeticException
is thrown.
The result of a floating-point division is determined by the rules of IEEE 754 arithmetic:
If the result is not NaN, the sign of the result is positive if both operands have the same sign, and negative if the operands have different signs.
Division of an infinity by a finite value results in a signed infinity. The sign is determined by the rule stated above.
Division of a finite value by an infinity results in a signed zero. The sign is determined by the rule stated above.
Division of a zero by a zero results in NaN; division of zero by any other finite value results in a signed zero. The sign is determined by the rule stated above.
Division of a nonzero finite value by a zero results in a signed infinity. The sign is determined by the rule stated above.
In the remaining cases, where neither an infinity nor NaN is involved, the exact mathematical quotient is computed. A floating-point value set is then chosen:
If the division expression is FP-strict (§15.4):
If the division expression is not FP-strict:
If the type
of the division
expression is float
, then either the
float value set or
the float-extended-exponent value set may be chosen, at
the whim of the implementation.
If the type
of the division
expression is double
, then either the
double value set
or the double-extended-exponent value set may be chosen,
at the whim of the implementation.
Next, a value must be chosen from the chosen value set to represent the quotient.
If the magnitude of the quotient is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign.
Otherwise, the quotient is rounded to the nearest value in the chosen value set using IEEE 754 round-to-nearest mode. The Java programming language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
Despite
the fact that overflow, underflow, division by zero, or loss of
information may occur, evaluation of a floating-point division
operator /
never throws a run-time exception.
The
binary %
operator is said to yield the remainder of
its operands from an implied division; the left-hand operand is
the dividend and the right-hand operand is
the divisor.
In C and C++, the remainder operator accepts only integral operands, but in the Java programming language, it also accepts floating-point operands.
The
remainder operation for operands that are integers after binary
numeric promotion (§5.6.2) produces a result
value such that (a/b)*b+(a%b)
is equal
to a
.
This
identity holds even in the special case that the dividend is the
negative integer of largest possible magnitude for its type and the
divisor is -1
(the remainder
is 0
).
It follows from this rule that the result of the remainder operation can be negative only if the dividend is negative, and can be positive only if the dividend is positive. Moreover, the magnitude of the result is always less than the magnitude of the divisor.
If the
value of the divisor for an integer remainder operator
is 0
, then an ArithmeticException
is
thrown.
Example 15.17.3-1. Integer Remainder Operator
class Test1 { public static void main(String[] args) { int a = 5%3; // 2 int b = 5/3; // 1 System.out.println("5%3 produces " + a + " (note that 5/3 produces " + b + ")"); int c = 5%(-3); // 2 int d = 5/(-3); // -1 System.out.println("5%(-3) produces " + c + " (note that 5/(-3) produces " + d + ")"); int e = (-5)%3; // -2 int f = (-5)/3; // -1 System.out.println("(-5)%3 produces " + e + " (note that (-5)/3 produces " + f + ")"); int g = (-5)%(-3); // -2 int h = (-5)/(-3); // 1 System.out.println("(-5)%(-3) produces " + g + " (note that (-5)/(-3) produces " + h + ")"); } }
This program produces the output:
5%3 produces 2 (note that 5/3 produces 1) 5%(-3) produces 2 (note that 5/(-3) produces -1) (-5)%3 produces -2 (note that (-5)/3 produces -1) (-5)%(-3) produces -2 (note that (-5)/(-3) produces 1)
The result of a
floating-point remainder operation as computed by
the %
operator is not the same
as that produced by the remainder operation defined by IEEE 754. The
IEEE 754 remainder operation computes the remainder from a rounding
division, not a truncating division, and so its behavior
is not analogous to that of the usual integer
remainder operator. Instead, the Java programming language
defines %
on floating-point operations to behave in
a manner analogous to that of the integer remainder operator; this may
be compared with the C library function fmod
. The
IEEE 754 remainder operation may be computed by the library
routine Math.IEEEremainder
.
The result of a floating-point remainder operation is determined by the rules of IEEE 754 arithmetic:
If the result is not NaN, the sign of the result equals the sign of the dividend.
If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN.
If the dividend is finite and the divisor is an infinity, the result equals the dividend.
If the dividend is a zero and the divisor is finite, the result equals the dividend.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, the floating-point remainder r from the division of a dividend n by a divisor d is defined by the mathematical relation r = n - (d ⋅ q) where q is an integer that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d.
Evaluation of a floating-point remainder
operator %
never throws a run-time exception, even
if the right-hand operand is zero. Overflow, underflow, or loss of
precision cannot occur.
Example 15.17.3-2. Floating-Point Remainder Operator
class Test2 { public static void main(String[] args) { double a = 5.0%3.0; // 2.0 System.out.println("5.0%3.0 produces " + a); double b = 5.0%(-3.0); // 2.0 System.out.println("5.0%(-3.0) produces " + b); double c = (-5.0)%3.0; // -2.0 System.out.println("(-5.0)%3.0 produces " + c); double d = (-5.0)%(-3.0); // -2.0 System.out.println("(-5.0)%(-3.0) produces " + d); } }
This program produces the output:
5.0%3.0 produces 2.0 5.0%(-3.0) produces 2.0 (-5.0)%3.0 produces -2.0 (-5.0)%(-3.0) produces -2.0
The operators +
and
-
are called the additive operators.
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.28).
An implementation may choose to perform conversion
and concatenation in one step to avoid creating and then discarding an
intermediate String
object. To increase the performance of repeated
string concatenation, a Java compiler may use
the StringBuffer
class or a similar technique to
reduce the number of intermediate String
objects that are created by
evaluation of an expression.
For primitive types, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.
Example 15.18.1-1. String Concatenation
The example expression:
"The square root of 2 is " + Math.sqrt(2)
produces the result:
"The square root of 2 is 1.4142135623730952"
The +
operator is syntactically
left-associative, no matter whether it is determined by type analysis
to represent string concatenation or numeric addition. In some cases
care is required to get the desired result. For example, the
expression:
a + b + c
is always regarded as meaning:
(a + b) + c
Therefore the result of the expression:
1 + 2 + " fiddlers"
is:
"3 fiddlers"
but the result of:
"fiddlers " + 1 + 2
is:
"fiddlers 12"
Example 15.18.1-2. String Concatenation and Conditionals
In this jocular little example:
class Bottles { static void printSong(Object stuff, int n) { String plural = (n == 1) ? "" : "s"; loop: while (true) { System.out.println(n + " bottle" + plural + " of " + stuff + " on the wall,"); System.out.println(n + " bottle" + plural + " of " + stuff + ";"); System.out.println("You take one down " + "and pass it around:"); --n; plural = (n == 1) ? "" : "s"; if (n == 0) break loop; System.out.println(n + " bottle" + plural + " of " + stuff + " on the wall!"); System.out.println(); } System.out.println("No bottles of " + stuff + " on the wall!"); } public static void main(String[] args) { printSong("slime", 3); } }
the method printSong
will print a
version of a children's song. Popular values
for stuff
include "pop
" and
"beer
"; the most popular value
for n
is 100
. Here is the output
that results from running the program:
3 bottles of slime on the wall, 3 bottles of slime; You take one down and pass it around: 2 bottles of slime on the wall! 2 bottles of slime on the wall, 2 bottles of slime; You take one down and pass it around: 1 bottle of slime on the wall! 1 bottle of slime on the wall, 1 bottle of slime; You take one down and pass it around: No bottles of slime on the wall!
In the code, note the careful conditional generation
of the singular "bottle
" when appropriate rather
than the plural "bottles
"; note also how the string
concatenation operator was used to break the long constant
string:
"You take one down and pass it around:"
into two pieces to avoid an inconveniently long line in the source code.
The
binary +
operator performs addition when applied to
two operands of numeric type, producing the sum of the
operands.
The
binary -
operator performs subtraction, producing
the difference of two numeric operands.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
The type of an additive expression on numeric operands is the promoted type of its operands.
If this
promoted type is int
or long
, then integer
arithmetic is
performed.
If this
promoted type is float
or double
, then
floating-point arithmetic
is performed.
Addition is a commutative operation if the operand expressions have no side effects.
Integer addition is associative when the operands are all of the same type.
Floating-point addition is not associative.
If an integer addition overflows, then the result is the low-order bits of the mathematical sum as represented in some sufficiently large two's-complement format. If overflow occurs, then the sign of the result is not the same as the sign of the mathematical sum of the two operand values.
The result of a floating-point addition is determined using the following rules of IEEE 754 arithmetic:
The sum of two infinities of the same sign is the infinity of that sign.
The sum of an infinity and a finite value is equal to the infinite operand.
The sum of two zeros of the same sign is the zero of that sign.
The sum of a zero and a nonzero finite value is equal to the nonzero operand.
The sum of two nonzero finite values of the same magnitude and opposite sign is positive zero.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, and the operands have the same sign or have different magnitudes, the exact mathematical sum is computed. A floating-point value set is then chosen:
If the addition expression is FP-strict (§15.4):
If the addition expression is not FP-strict:
If the type
of the addition
expression is float
, then either the
float value set or
the float-extended-exponent value set may be chosen, at
the whim of the implementation.
If the type
of the addition
expression is double
, then either the
double value set
or the double-extended-exponent value set may be chosen,
at the whim of the implementation.
Next, a value must be chosen from the chosen value set to represent the sum.
If the magnitude of the sum is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign.
Otherwise, the sum is rounded to the nearest value in the chosen value set using IEEE 754 round-to-nearest mode. The Java programming language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
The
binary -
operator performs subtraction when applied
to two operands of numeric type, producing the difference of its
operands; the left-hand operand is the minuend
and the right-hand operand is
the subtrahend.
For
both integer and floating-point subtraction, it is always the case
that a-b
produces the same result
as a+(-b)
.
Note
that, for integer values, subtraction from zero is the same as
negation. However, for floating-point operands, subtraction from zero
is not the same as negation, because if x
is +0.0
, then 0.0-x
is +0.0
, but -x
is -0.0
.
Despite the fact that overflow, underflow, or loss of information may occur, evaluation of a numeric additive operator never throws a run-time exception.
The operators <<
(left
shift), >>
(signed right shift), and >>>
(unsigned right
shift) are called the shift operators. The
left-hand operand of a shift operator is the value to be shifted; the
right-hand operand specifies the shift distance.
The shift operators are syntactically left-associative (they group left-to-right).
Unary numeric promotion (§5.6.1) is performed on each operand separately. (Binary numeric promotion (§5.6.2) is not performed on the operands.)
It is a compile-time error if the type of each of the operands of a shift operator, after unary numeric promotion, is not a primitive integral type.
The type of the shift expression is the promoted type of the left-hand operand.
If the
promoted type of the left-hand operand is int
, 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.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
If the
promoted type of the operands is int
or long
,
then signed integer
comparison is performed.
If the
promoted type is float
or double
, then
floating-point comparison
is performed.
Comparison is carried out accurately on floating-point values, no matter what value sets their representing values were drawn from.
The result of a floating-point comparison, as determined by the specification of the IEEE 754 standard, is:
All values other than NaN are ordered, with negative infinity less than all finite values, and positive infinity greater than all finite values.
Positive zero and negative zero are considered equal.
For example, -0.0<0.0
is
false
, but -0.0<=0.0
is true
.
Note, however, that the
methods Math.min
and Math.max
treat negative zero as being
strictly smaller than positive zero.
Subject to these considerations for floating-point numbers, the following rules then hold for integer operands or for floating-point operands other than NaN:
The value produced by the <
operator is true
if the value of the left-hand operand is less
than the value of the right-hand operand, and otherwise is
false
.
The value produced by the
<=
operator is true
if the
value of the
left-hand operand is less than or equal to the value of the
right-hand operand, and otherwise is false
.
The value produced by the
>
operator is true
if the value
of the left-hand
operand is greater than the value of the right-hand operand, and
otherwise is false
.
The value produced by the
>=
operator is true
if the
value of the
left-hand operand is greater than or equal to the value of the
right-hand operand, and otherwise is false
.
The type
of the RelationalExpression operand of the
instanceof
operator must be a reference type or the null type;
otherwise, a compile-time error occurs.
It is a
compile-time error if the ReferenceType mentioned
after the instanceof
operator does not denote a reference type that
is reifiable (§4.7).
If a cast
(§15.16) of
the RelationalExpression to
the ReferenceType would be rejected as a
compile-time error, then the instanceof
relational expression
likewise produces a compile-time error. In such a situation, the
result of the instanceof
expression could never be true.
At run
time, the result of the instanceof
operator is true
if the value
of the RelationalExpression is not null
and the
reference could be cast to the ReferenceType
without raising a ClassCastException
. Otherwise the result is false
.
Example 15.20.2-1. The instanceof
Operator
class Point { int x, y; } class Element { int atomicNumber; } class Test { public static void main(String[] args) { Point p = new Point(); Element e = new Element(); if (e instanceof Point) { // compile-time error System.out.println("I get your point!"); p = (Point)e; // compile-time error } } }
This program results in two compile-time errors. The
cast (Point)e
is incorrect because no instance
of Element
or any of its possible subclasses (none
are shown here) could possibly be an instance of any subclass
of Point
. The instanceof
expression is
incorrect
for exactly the same reason. If, on the other hand, the
class Point
were a subclass
of Element
(an admittedly strange notion in this
example):
class Point extends Element { int x, y; }
then the cast would be possible, though it would
require a run-time check, and the instanceof
expression would then
be sensible and valid. The cast (Point)e
would
never raise an exception because it would not be executed if the value
of e
could not correctly be cast to
type Point
.
The
operators ==
(equal to) and !=
(not equal to) are called the equality
operators.
The equality operators are syntactically left-associative (they group left-to-right).
However, this fact is essentially never useful. For
example, a==b==c
parses
as (a==b)==c
. The result type
of a==b
is always boolean
,
and c
must therefore be of type boolean
or a
compile-time error occurs. Thus, a==b==c
does not
test to see whether a
, b
,
and c
are all equal.
The equality operators are commutative if the operand expressions have no side effects.
The
equality operators are analogous to the relational operators except
for their lower precedence. Thus, a<b==c<d
is
true
whenever a<b
and
c<d
have the same truth value.
The
equality operators may be used to compare two operands that are
convertible (§5.1.8) to numeric type, or two
operands of type boolean
or Boolean
, or two
operands that are each
of either reference type or the null type. All other cases result in a
compile-time error.
The type
of an equality expression is always boolean
.
In all
cases, a!=b
produces the same result
as !(a==b)
.
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
If the
promoted type of the operands is int
or long
,
then an integer
equality test is performed.
If the
promoted type is float
or double
, then a
floating-point equality
test is performed.
Comparison is carried out accurately on floating-point values, no matter what value sets their representing values were drawn from.
Floating-point equality testing is performed in accordance with the rules of the IEEE 754 standard:
If either operand is NaN, then the
result of ==
is false
but the
result
of !=
is true
.
Indeed, the
test x!=x
is true
if and only if
the value
of x
is NaN.
The methods Float.isNaN
and Double.isNaN
may also be used to test
whether a value is NaN.
Positive zero and negative zero are considered equal.
For example, -0.0==0.0
is
true
.
Otherwise, two distinct floating-point values are considered unequal by the equality operators.
In particular, there is one value representing positive infinity and one value representing negative infinity; each compares equal only to itself, and each compares unequal to all other values.
Subject to these considerations for floating-point numbers, the following rules then hold for integer operands or for floating-point operands other than NaN:
The value produced by
the ==
operator is true
if the
value of the
left-hand operand is equal to the value of the right-hand operand;
otherwise, the result is false
.
The value produced by
the !=
operator is true
if the
value of the
left-hand operand is not equal to the value of the right-hand
operand; otherwise, the result is false
.
If the
operands of an equality operator are both of type boolean
, or if one
operand is of type boolean
and the other is of type Boolean
,
then
the operation is boolean equality.
The boolean equality operators are associative.
If one
of the operands is of type Boolean
, it is subjected to unboxing
conversion (§5.1.8).
The
result of ==
is true
if the operands (after
any
required unboxing conversion) are both true
or both false
;
otherwise, the result is false
.
The
result of !=
is false
if the operands are both
true
or both false
; otherwise, the result is
true
.
Thus !=
behaves the same
as ^
(§15.22.2)
when applied
to boolean
operands.
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
It is a
compile-time error if it is impossible to convert the type of either
operand to the type of the other by a casting conversion
(§5.5). The
run-time values of the two operands
would necessarily be unequal (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).
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.2).
The type of the bitwise operator expression is the promoted type of the operands.
For &
, the result
value is the bitwise AND of the operand values.
For ^
, the result value
is
the bitwise exclusive OR of the operand values.
For |
, the result value
is
the bitwise inclusive OR of the operand values.
For example, the result of the expression:
0xff00 & 0xf0f0
is:
0xf000
The result of the expression:
0xff00 ^ 0xf0f0
is:
0x0ff0
The result of the expression:
0xff00 | 0xf0f0
is:
0xfff0
When
both operands of a &
, ^
,
or |
operator are of type boolean
or Boolean
,
then the type of the bitwise operator expression is boolean
. In all
cases, the operands are subject to unboxing conversion
(§5.1.8) as
necessary.
For &
, the result
value is true
if both operand values are true
;
otherwise, the
result is false
.
For ^
, the result value
is
true
if the operand values are different; otherwise, the result is
false
.
For |
, the result value
is
false
if both operand values are false
;
otherwise, the result is
true
.
The
conditional-and operator &&
is like &
(§15.22.2), but evaluates its
right-hand operand
only if the value of its left-hand operand is true
.
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
(§<IP_ADDRESS>)
has return type boolean
or Boolean
.
Note that, for a generic method, this is the type before instantiating the method's type arguments.
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 (§<IP_ADDRESS>) has a return type that is convertible to a numeric type.
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.28)
of type int
whose value is
representable in type T, then the type of the conditional
expression is T.
If one of the operands is of
type T, where T is Byte
, Short
, or Character
, and the
other operand is a constant expression of type int
whose value
is representable in the type U which is the result of applying
unboxing conversion to T, then the type of the conditional
expression is U.
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
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.
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
which is
incompatible with the return type of <
? extends Number>
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.
A compile-time error occurs if the type of the right-hand operand cannot be converted to the type of the variable by assignment conversion (§5.2).
At run time, the expression is evaluated in one of three ways.
If the
left-hand operand expression is a field access expression 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, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the array component.
If TC is a reference type, then SC may not be the same as TC, but rather a type that extends or implements TC.
Let RC be the class of the object referred to by the value of the right-hand operand at run time.
A Java compiler may be able to prove at compile time that
the array component will be of type TC exactly (for
example, TC might be final
). But if a Java compiler
cannot prove at compile time that the array component will
be of type TC exactly, then a check must be performed
at
run time to ensure that the class RC is assignment
compatible (§5.2) with the
actual type
SC of the array component.
This check is similar to a narrowing cast
(§5.5,
§15.16),
except that if the check fails, an ArrayStoreException
is thrown rather
than a ClassCastException
.
If class RC is not assignable to type SC, then no
assignment occurs and an ArrayStoreException
is
thrown.
Otherwise, the reference value of the right-hand operand is stored into the selected array component.
Otherwise, three steps are required:
First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the value of the right-hand operand is converted to the type of the left-hand variable, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.
Example 15.26.1-1. Simple Assignment To An Array Component
class ArrayReferenceThrow extends RuntimeException { } class IndexThrow extends RuntimeException { } class RightHandSideThrow extends RuntimeException { } class IllustrateSimpleArrayAssignment { static Object[] objects = { new Object(), new Object() }; static Thread[] threads = { new Thread(), new Thread() }; static Object[] arrayThrow() { throw new ArrayReferenceThrow(); } static int indexThrow() { throw new IndexThrow(); } static Thread rightThrow() { throw new RightHandSideThrow(); } static String name(Object q) { String sq = q.getClass().getName(); int k = sq.lastIndexOf('.'); return (k < 0) ? sq : sq.substring(k+1); } static void testFour(Object[] x, int j, Object y) { String sx = x == null ? "null" : name(x[0]) + "s"; String sy = name(y); System.out.println(); try { System.out.print(sx + "[throw]=throw => "); x[indexThrow()] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[throw]=" + sy + " => "); x[indexThrow()] = y; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]=throw => "); x[j] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]=" + sy + " => "); x[j] = y; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } } public static void main(String[] args) { try { System.out.print("throw[throw]=throw => "); arrayThrow()[indexThrow()] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]=Thread => "); arrayThrow()[indexThrow()] = new Thread(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]=throw => "); arrayThrow()[1] = rightThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]=Thread => "); arrayThrow()[1] = new Thread(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } testFour(null, 1, new StringBuffer()); testFour(null, 9, new Thread()); testFour(objects, 1, new StringBuffer()); testFour(objects, 1, new Thread()); testFour(objects, 9, new StringBuffer()); testFour(objects, 9, new Thread()); testFour(threads, 1, new StringBuffer()); testFour(threads, 1, new Thread()); testFour(threads, 9, new StringBuffer()); testFour(threads, 9, new Thread()); } }
This program produces the output:
throw[throw]=throw => ArrayReferenceThrow throw[throw]=Thread => ArrayReferenceThrow throw[1]=throw => ArrayReferenceThrow throw[1]=Thread => ArrayReferenceThrow null[throw]=throw => IndexThrow null[throw]=StringBuffer => IndexThrow null[1]=throw => RightHandSideThrow null[1]=StringBuffer => NullPointerException null[throw]=throw => IndexThrow null[throw]=Thread => IndexThrow null[9]=throw => RightHandSideThrow null[9]=Thread => NullPointerException Objects[throw]=throw => IndexThrow Objects[throw]=StringBuffer => IndexThrow Objects[1]=throw => RightHandSideThrow Objects[1]=StringBuffer => Okay! Objects[throw]=throw => IndexThrow Objects[throw]=Thread => IndexThrow Objects[1]=throw => RightHandSideThrow Objects[1]=Thread => Okay! Objects[throw]=throw => IndexThrow Objects[throw]=StringBuffer => IndexThrow Objects[9]=throw => RightHandSideThrow Objects[9]=StringBuffer => ArrayIndexOutOfBoundsException Objects[throw]=throw => IndexThrow Objects[throw]=Thread => IndexThrow Objects[9]=throw => RightHandSideThrow Objects[9]=Thread => ArrayIndexOutOfBoundsException Threads[throw]=throw => IndexThrow Threads[throw]=StringBuffer => IndexThrow Threads[1]=throw => RightHandSideThrow Threads[1]=StringBuffer => ArrayStoreException Threads[throw]=throw => IndexThrow Threads[throw]=Thread => IndexThrow Threads[1]=throw => RightHandSideThrow Threads[1]=Thread => Okay! Threads[throw]=throw => IndexThrow Threads[throw]=StringBuffer => IndexThrow Threads[9]=throw => RightHandSideThrow Threads[9]=StringBuffer => ArrayIndexOutOfBoundsException Threads[throw]=throw => IndexThrow Threads[throw]=Thread => IndexThrow Threads[9]=throw => RightHandSideThrow Threads[9]=Thread => ArrayIndexOutOfBoundsException
The most interesting case of the lot is thirteenth from the end:
Threads[1]=StringBuffer => ArrayStoreException
which indicates that the attempt to store a
reference to a StringBuffer
into an array whose
components are of type Thread
throws an ArrayStoreException
.
The code is
type-correct at compile time: the assignment has a left-hand side of
type Object[]
and a right-hand side of type
Object
. At run time, the first actual argument to
method testFour
is a reference to an instance of
"array of Thread
" and the third actual argument is a reference to
an
instance of class StringBuffer
.
A
compound assignment expression of the form E1 op=
E2
is equivalent to E1 = (T) ((E1) op
(E2))
, where T is the type of E1
,
except that E1
is evaluated only once.
For example, the following code is correct:
short x = 3; x += 4.6;
and results in x
having the
value 7
because it is equivalent to:
short x = 3; x = (short)(x + 4.6);
At run time, the expression is evaluated in one of two ways.
If the left-hand operand expression is not an array access expression, then:
First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the saved value of the left-hand variable and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator. If this operation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the result of the binary operation is converted to the type of the left-hand variable, subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.
If the left-hand operand expression is an array access expression (§15.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, subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the array component.
If T is a reference type, then it must be
String
. Because class String
is a final
class, S
must also be String
.
Therefore the run-time check that is sometimes required for the simple assignment operator is never required for a compound assignment operator.
The saved value of the array component and the value of the
right-hand operand are used to perform the binary operation
(string concatenation) indicated by the compound assignment
operator (which is necessarily +=
). If this
operation completes abruptly, then the assignment expression
completes abruptly for the same reason and no assignment
occurs.
Otherwise, the String
result of the binary
operation is
stored into the array component.
Example 15.26.2-1. Compound Assignment To An Array Component
class ArrayReferenceThrow extends RuntimeException { } class IndexThrow extends RuntimeException { } class RightHandSideThrow extends RuntimeException { } class IllustrateCompoundArrayAssignment { static String[] strings = { "Simon", "Garfunkel" }; static double[] doubles = { Math.E, Math.PI }; static String[] stringsThrow() { throw new ArrayReferenceThrow(); } static double[] doublesThrow() { throw new ArrayReferenceThrow(); } static int indexThrow() { throw new IndexThrow(); } static String stringThrow() { throw new RightHandSideThrow(); } static double doubleThrow() { throw new RightHandSideThrow(); } static String name(Object q) { String sq = q.getClass().getName(); int k = sq.lastIndexOf('.'); return (k < 0) ? sq : sq.substring(k+1); } static void testEight(String[] x, double[] z, int j) { String sx = (x == null) ? "null" : "Strings"; String sz = (z == null) ? "null" : "doubles"; System.out.println(); try { System.out.print(sx + "[throw]+=throw => "); x[indexThrow()] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[throw]+=throw => "); z[indexThrow()] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[throw]+=\"heh\" => "); x[indexThrow()] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[throw]+=12345 => "); z[indexThrow()] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]+=throw => "); x[j] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[" + j + "]+=throw => "); z[j] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]+=\"heh\" => "); x[j] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[" + j + "]+=12345 => "); z[j] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } } public static void main(String[] args) { try { System.out.print("throw[throw]+=throw => "); stringsThrow()[indexThrow()] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=throw => "); doublesThrow()[indexThrow()] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=\"heh\" => "); stringsThrow()[indexThrow()] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=12345 => "); doublesThrow()[indexThrow()] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=throw => "); stringsThrow()[1] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=throw => "); doublesThrow()[1] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=\"heh\" => "); stringsThrow()[1] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[1]+=12345 => "); doublesThrow()[1] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } testEight(null, null, 1); testEight(null, null, 9); testEight(strings, doubles, 1); testEight(strings, doubles, 9); } }
This program produces the output:
throw[throw]+=throw => ArrayReferenceThrow throw[throw]+=throw => ArrayReferenceThrow throw[throw]+="heh" => ArrayReferenceThrow throw[throw]+=12345 => ArrayReferenceThrow throw[1]+=throw => ArrayReferenceThrow throw[1]+=throw => ArrayReferenceThrow throw[1]+="heh" => ArrayReferenceThrow throw[1]+=12345 => ArrayReferenceThrow null[throw]+=throw => IndexThrow null[throw]+=throw => IndexThrow null[throw]+="heh" => IndexThrow null[throw]+=12345 => IndexThrow null[1]+=throw => NullPointerException null[1]+=throw => NullPointerException null[1]+="heh" => NullPointerException null[1]+=12345 => NullPointerException null[throw]+=throw => IndexThrow null[throw]+=throw => IndexThrow null[throw]+="heh" => IndexThrow null[throw]+=12345 => IndexThrow null[9]+=throw => NullPointerException null[9]+=throw => NullPointerException null[9]+="heh" => NullPointerException null[9]+=12345 => NullPointerException Strings[throw]+=throw => IndexThrow doubles[throw]+=throw => IndexThrow Strings[throw]+="heh" => IndexThrow doubles[throw]+=12345 => IndexThrow Strings[1]+=throw => RightHandSideThrow doubles[1]+=throw => RightHandSideThrow Strings[1]+="heh" => Okay! doubles[1]+=12345 => Okay! Strings[throw]+=throw => IndexThrow doubles[throw]+=throw => IndexThrow Strings[throw]+="heh" => IndexThrow doubles[throw]+=12345 => IndexThrow Strings[9]+=throw => ArrayIndexOutOfBoundsException doubles[9]+=throw => ArrayIndexOutOfBoundsException Strings[9]+="heh" => ArrayIndexOutOfBoundsException doubles[9]+=12345 => ArrayIndexOutOfBoundsException
The most interesting cases of the lot are eleventh and twelfth from the end:
Strings[1]+=throw => RightHandSideThrow doubles[1]+=throw => RightHandSideThrow
They are the cases where a right-hand side that throws an exception actually gets to throw the exception; moreover, they are the only such cases in the lot. This demonstrates that the evaluation of the right-hand operand indeed occurs after the checks for a null array reference value and an out-of-bounds index value.
Example 15.26.2-2. Value Of Left-Hand Side Of Compound Assignment Is Saved Before Evaluation Of Right-Hand Side
class Test { public static void main(String[] args) { int k = 1; int[] a = { 1 }; k += (k = 4) * (k + 2); a[0] += (a[0] = 4) * (a[0] + 2); System.out.println("k==" + k + " and a[0]==" + a[0]); } }
This program produces the output:
k==25 and a[0]==25
The value 1
of k
is saved
by
the compound assignment operator +=
before its
right-hand operand (k = 4) * (k + 2)
is
evaluated. Evaluation of this right-hand operand then
assigns 4
to k
, calculates the
value 6
for k + 2
, and then
multiplies 4
by 6
to
get 24
. This is added to the saved value 1
to
get 25
, which is then stored
into k
by the +=
operator. An
identical analysis applies to the case that
uses a[0]
.
In short, the statements:
k += (k = 4) * (k + 2); a[0] += (a[0] = 4) * (a[0] + 2);
behave in exactly the same manner as the statements:
k = k + (k = 4) * (k + 2); a[0] = a[0] + (a[0] = 4) * (a[0] + 2);
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 may have either declared types or inferred types. These styles cannot be mixed: it is not possible for a lambda expression to declare the types of some of its parameters but leave others to be inferred. Only parameters with declared types can have modifiers.
The following productions from §4.3, §8.3, and §8.4.1 are shown here for convenience:
Receiver parameters are not permitted in the FormalParameters of a lambda expression, as specified in §8.4.1.
 A lambda expression with zero parameters is explicitly typed.
If the formal parameters have inferred types, then these types are derived (§15.27.3) from the functional interface type targeted by the lambda expression.
The syntax for formal parameters with declared types is the same as the syntax for the parameters of a method declaration (§8.4.1).
The declared type of a formal parameter depends on whether it is a variable arity parameter:
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 specified by §10.2. (Note that "mixed notation" is not permitted for variable arity parameters.)
No distinction is made between the following lambda parameter lists:
(int... x)->
.. (int[] x)->
..
Consistent with the rules for overriding, either can
be used, whether the functional interface's abstract method is fixed
arity or variable arity. Since lambda expressions are never directly
invoked, introducing int...
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.
The rules for annotation modifiers on 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 to use mixed array notation (§10.2) for a variable arity parameter.
The scope and shadowing of a formal parameter declaration is specified in §6.3 and §6.4.
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.)
It is a
compile-time error if a lambda parameter has the name _
(that is, a single underscore character).
The use of the variable name _
in any
context is discouraged. Future versions of the Java programming language may reserve
this name as a keyword and/or give it special semantics.
It is a compile-time error if a receiver parameter (§8.4.1) appears in the FormalParameters of a lambda expression.
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 VariableDeclaratorId or the InferredFormalParameterList may be used as a simple name in the lambda body to refer to the formal parameter.
A
lambda parameter of type float
always contains an element of the
float value set (§4.2.3);
similarly, a lambda
parameter of type double
always contains an element of the double
value set. It is not permitted for a lambda parameter of type float
to contain an element of the float-extended-exponent value set that is
not also an element of the float value set, nor for a lambda parameter
of type double
to contain an element of the double-extended-exponent
value set that is not also an element of the double value set.
When the parameter types of a lambda expression are inferred, the same lambda body can be interpreted in different ways, depending on the context in which it appears. Specifically, the types of expressions in the body, the checked exceptions thrown by the body, and the type correctness of code in the body all depend on the parameters' inferred types. This implies that inference of parameter types must occur "before" attempting to type-check the body of the lambda expression.
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.21) 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