15.13 Method Reference Expressions

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.

MethodReference:
   ExpressionName :: [TypeArguments] Identifier
   ReferenceType :: [TypeArguments] Identifier
   Primary :: [TypeArguments] Identifier
   super :: [TypeArguments] Identifier
   TypeName . super :: [TypeArguments] Identifier
   ClassType :: [TypeArguments] new
   ArrayType :: 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 (§15.12.4.1) may be provided by the method reference expression using an ExpressionName, a Primary, or super, or it may be provided later when the method is invoked. The immediately enclosing instance of a new inner class instance (§15.9.2) must be provided by a lexically enclosing instance of this8.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:

Click here to view code image

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:

Click here to view code image

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:

Click here to view code image

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.

15.13.1 Compile-Time Declaration of a Method Reference

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 §15.12.2.1.

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 §15.12.2.1.

– 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 §15.12.2.1.

• Finally, if there are no potentially applicable methods, then there is no compiletime declaration.

Otherwise, given a targeted function type with parameter types P1, ..., Pn and a set of potentially applicable methods, the compile-time declaration is selected as follows:

– If the method reference expression has the form ReferenceType :: [TypeArguments] Identifier, then two searches for a most specific applicable method are performed. Each search is as specified in §15.12.2.2 through §15.12.2.5, with the clarifications below. Each search may produce a method or, in the case of an error as specified in §15.12.2.2 through §15.12.2.5, 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 §15.12.2.2, §15.12.2.3, or §15.12.2.4 during the second search, then the compile-time declaration is the result of the first search.

Otherwise, if no static method is applicable by §15.12.2.2, §15.12.2.3, or §15.12.2.4 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.

Otherwise, there is no compile-time declaration.

– For all other forms of method reference expression, one search for a most specific applicable method is performed. The search is as specified in §15.12.2.2 through §15.12.2.5, with the clarifications below.

The method reference is treated as if it were an invocation with argument expressions of types P1, ..., Pn; the type arguments, if any, are given by the method reference expression.

If the search results in an error as specified in §15.12.2.2 through §15.12.2.5, or if the most specific applicable method is static, there is no compile-time declaration.

Otherwise, the compile-time declaration is the most specific applicable method.

It is a compile-time error if a method reference expression has the form ReferenceType :: [TypeArguments] Identifier, and the compile-time declaration is static, and ReferenceType is not a simple or qualified name (§6.2).

It is a compile-time error if the method reference expression has the form super :: [TypeArguments] Identifier or TypeName . super :: [TypeArguments] Identifier, and the compile-time declaration is abstract.

It is a compile-time error if the method reference expression has the form 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 a 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:

Click here to view code image

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:

Click here to view code image

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:

Click here to view code image

interface Fun<T,R> { R apply(T arg); }

class C {
    int size() { return 0; }
    int size(Object arg) { return 0; }
    int size(C arg) { return 0; }

    void test() {
        Fun<C, Integer> f1 = C::size;
          // OK: reference is to instance method size()
    }
}

For convenience, when the name of a generic type is used to refer to an instance method (where the receiver becomes the first parameter), the target type is used to determine the type arguments. This facilitates usage like Pair::first in place of Pair<String,Integer>::first. Similarly, a method reference like Pair::new is treated like a “diamond” instance creation (new Pair<>()). Because the “diamond” is implicit, this form does not instantiate a raw type; in fact, there is no way to express a reference to the constructor of a raw type.

For some method reference expressions, there is only one possible compile-time declaration with only one possible invocation type (§15.12.2.6), regardless of the targeted function type. Such method reference expressions are said to be exact. A method reference expression that is not exact is said to be inexact.

A method reference expression ending with Identifier is exact if it satisfies all of the following:

• If the method reference expression has the form ReferenceType :: [TypeArguments] Identifier, then ReferenceType does not denote a raw type.

• The type to search has exactly one member method with the name Identifier that is accessible to the class or interface in which the method reference expression appears.

• This method is not variable arity (§8.4.1).

• If this method is generic (§8.4.4), then the method reference expression provides TypeArguments.

A method reference expression of the form ClassType :: [TypeArguments] new is exact if it satisfies all of the following:

• The type denoted by ClassType is not raw, or is a non-static member type of a raw type.

• The type denoted by ClassType has exactly one constructor that is accessible to the class or interface in which the method reference expression appears.

• This constructor is not variable arity.

• If this constructor is generic, then the method reference expression provides TypeArguments.

A method reference expression of the form ArrayType :: new is always exact.

15.13.2 Type of a Method Reference

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.

• Otherwise, the ground target type is 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.

• One of the following is true:

– The result of the function type is void.

– The result of the function type is R, and the result of applying capture conversion (§5.1.10) to the return type of the invocation type (§15.12.2.6) of the chosen compile-time declaration is R’ (where R is the target type that may be used to infer R’), and neither R nor R’ is void, and R’ is compatible with R in an assignment context.

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 §8.4.8.3 may occur.

For each checked exception type X listed in the throws clause of the invocation type of the compile-time declaration, X or a superclass of X must be mentioned in the throws clause of the function type of U, or a compile-time error occurs.

The key idea driving the compatibility definition is that a method reference is compatible if and only if the equivalent lambda expression (x, y, z) -> exp.<T1, T2>method(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.)

These compatibility rules provide a convenient facility for converting from one functional interface to another:

Click here to view code image

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:

Click here to view code image

interface ListFactory {
    <T> List<T> make();
}

ListFactory lf  = ArrayList::new;
List<String> ls = lf.make();
List<Number> ln = lf.make();

15.13.3 Run-Time Evaluation of Method References

At run time, evaluation of a method reference expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a method reference expression is distinct from invocation of the method itself.

First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to null, a NullPointerException is raised, and the method reference expression completes abruptly. If the subexpression completes abruptly, the method reference expression completes abruptly for the same reason.

Next, either a new instance of a class with the properties below is allocated and initialized, or an existing instance of a class with the properties below is referenced. If a new instance is to be created, but there is insufficient space to allocate the object, evaluation of the method reference expression completes abruptly by throwing an OutOfMemoryError.

The value of a method reference expression is a reference to an instance of a class with the following properties:

• The class implements the targeted functional interface type and, if the target type is an intersection type, every other interface type mentioned in the intersection.

• Where the method reference expression has type U, for each non-static member method m of U:

If the function type of U has a subsignature of the signature of m, then the class declares an invocation method that overrides m. The invocation method’s body invokes the referenced method, creates a class instance, or creates an array, as described below. If the invocation method’s result is not void, then the body returns the result of the method invocation or object creation, after any necessary assignment conversions (§5.2).

If the erasure of the type of a method being overridden differs in its signature from the erasure of the function type of U, then before the method invocation or object creation, an invocation method’s body checks that each argument value is an instance of a subclass or subinterface of the erasure of the corresponding parameter type in the function type of U; if not, a ClassCastException is thrown.

• The class overrides no other methods of the functional interface type or other interface types mentioned above, although it may override methods of the Object class.

The body of an invocation method depends on the form of the method reference expression, as follows:

• If the form is ExpressionName :: [TypeArguments] Identifier or Primary :: [TypeArguments] Identifier, then the body of the invocation method has the effect of a method invocation expression for a compile-time declaration which is the compile-time declaration of the method reference expression. Run-time evaluation of the method invocation expression is as specified in §15.12.4.3, §15.12.4.4, and §15.12.4.5, where:

– The invocation mode is derived from the compile-time declaration as specified in §15.12.3.

– The target reference is the value of ExpressionName or Primary, as determined when the method reference expression was evaluated.

– The arguments to the method invocation expression are the formal parameters of the invocation method.

• If the form is ReferenceType :: [TypeArguments] Identifier, the body of the invocation method similarly has the effect of a method invocation expression for a compile-time declaration which is the compile-time declaration of the method reference expression. Run-time evaluation of the method invocation expression is as specified in §15.12.4.3, §15.12.4.4, and §15.12.4.5, where:

– The invocation mode is derived from the compile-time declaration as specified in §15.12.3.

– If the compile-time declaration is an instance method, then the target reference is the first formal parameter of the invocation method. Otherwise, there is no target reference.

– If the compile-time declaration is an instance method, then the arguments to the method invocation expression (if any) are the second and subsequent formal parameters of the invocation method. Otherwise, the arguments to the method invocation expression are the formal parameters of the invocation method.

• If the form is super :: [TypeArguments] Identifier or TypeName . super :: [TypeArguments] Identifier, the body of the invocation method has the effect of a method invocation expression for a compile-time declaration which is the compile-time declaration of the method reference expression. Run-time evaluation of the method invocation expression is as specified in §15.12.4.3, §15.12.4.4, and §15.12.4.5, where:

– The invocation mode is super.

– 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:

– The enclosing instance for the new object, if any, is derived from the site of the method reference expression, as specified in §15.9.2.

– The constructor to invoke is the constructor that corresponds to the compiletime declaration of the method reference (§15.13.1).

• 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 static15.13.1), the evaluation of the method reference expression is simpler - a null Primary always raises a NullPointerException.

15.14 Postfix Expressions

Postfix expressions include uses of the postfix ++ and -- operators. Names are not considered to be primary expressions (§15.8), but are handled separately in the grammar to avoid certain ambiguities. They become interchangeable only here, at the level of precedence of postfix expressions.

PostfixExpression:
    Primary
    ExpressionName
    PostIncrementExpression
    PostDecrementExpression

15.14.1 Expression Names

The rules for evaluating expression names are given in §6.5.6.

15.14.2 Postfix Increment Operator ++

A postfix expression followed by a ++ operator is a postfix increment expression.

PostIncrementExpression:
    PostfixExpression ++

The result of the postfix expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.

The type of the postfix increment expression is the type of the variable. The result of the postfix increment expression is not a variable, but a value.

At run time, if evaluation of the operand expression completes abruptly, then the postfix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. Before the addition, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion (§5.1.3) and/or subjected to boxing conversion (§5.1.7) to the type of the variable before it is stored. The value of the postfix increment expression is the value of the variable before the new value is stored.

Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the sum prior to its being stored in the variable.

A variable that is declared final cannot be incremented because when an access of such a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a postfix increment operator.

15.14.3 Postfix Decrement Operator --

A postfix expression followed by a -- operator is a postfix decrement expression.

PostDecrementExpression:
    PostfixExpression --

The result of the postfix expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.

The type of the postfix decrement expression is the type of the variable. The result of the postfix decrement expression is not a variable, but a value.

At run time, if evaluation of the operand expression completes abruptly, then the postfix decrement expression completes abruptly for the same reason and no decrementation occurs. Otherwise, the value 1 is subtracted from the value of the variable and the difference is stored back into the variable. Before the subtraction, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the difference is narrowed by a narrowing primitive conversion (§5.1.3) and/or subjected to boxing conversion (§5.1.7) to the type of the variable before it is stored. The value of the postfix decrement expression is the value of the variable before the new value is stored.

Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the difference prior to its being stored in the variable.

A variable that is declared final cannot be decremented because when an access of such a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a postfix decrement operator.

15.15 Unary Operators

The operators +, -, ++, --, ~, !, and the cast operator (§15.16) are called the unary operators.

UnaryExpression:
    PreIncrementExpression
    PreDecrementExpression
    + UnaryExpression
    - UnaryExpression
    UnaryExpressionNotPlusMinus

PreIncrementExpression:
    ++ UnaryExpression

PreDecrementExpression:
    -- UnaryExpression

UnaryExpressionNotPlusMinus:
    PostfixExpression
    ~ UnaryExpression
    ! UnaryExpression
    CastExpression

The following production from §15.16 is shown here for convenience:

CastExpression:
    ( PrimitiveType ) UnaryExpression
    ( ReferenceType {AdditionalBound} ) UnaryExpressionNotPlusMinus
   ( ReferenceType {AdditionalBound} ) LambdaExpression

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

15.15.1 Prefix Increment Operator ++

A unary expression preceded by a ++ operator is a prefix increment expression.

The result of the unary expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.

The type of the prefix increment expression is the type of the variable. The result of the prefix increment expression is not a variable, but a value.

At run time, if evaluation of the operand expression completes abruptly, then the prefix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. Before the addition, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion (§5.1.3) and/or subjected to boxing conversion (§5.1.7) to the type of the variable before it is stored. The value of the prefix increment expression is the value of the variable after the new value is stored.

Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, value set conversion is applied to the sum prior to its being stored in the variable.

A variable that is declared final cannot be incremented because when an access of such a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a prefix increment operator.

15.15.2 Prefix Decrement Operator --

A unary expression preceded by a -- operator is a prefix decrement expression.

The result of the unary expression must be a variable of a type that is convertible (§5.1.8) to a numeric type, or a compile-time error occurs.

The type of the prefix decrement expression is the type of the variable. The result of the prefix decrement expression is not a variable, but a value.

At run time, if evaluation of the operand expression completes abruptly, then the prefix decrement expression completes abruptly for the same reason and no decrementation occurs. Otherwise, the value 1 is subtracted from the value of the variable and the difference is stored back into the variable. Before the subtraction, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the difference is narrowed by a narrowing primitive conversion (§5.1.3) and/or subjected to boxing conversion (§5.1.7) to the type of the variable before it is stored. The value of the prefix decrement expression is the value of the variable after the new value is stored.

Note that the binary numeric promotion mentioned above may include unboxing conversion (§5.1.8) and value set conversion (§5.1.13). If necessary, format conversion is applied to the difference prior to its being stored in the variable.

A variable that is declared final cannot be decremented because when an access of such a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a prefix decrement operator.

15.15.3 Unary Plus 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.

15.15.4 Unary Minus 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 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.

15.15.5 Bitwise Complement Operator ~

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.

15.15.6 Logical Complement Operator !

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.

15.16 Cast Expressions

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.

CastExpression:
    ( PrimitiveType ) UnaryExpression
    ( ReferenceType {AdditionalBound} ) UnaryExpressionNotPlusMinus
    ( ReferenceType {AdditionalBound} ) LambdaExpression

The following production from §4.4 is shown here for convenience:

AdditionalBound:
    & InterfaceType

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:

ReferenceType must denote a class or interface type.

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

15.17 Multiplicative Operators

The operators *, /, and % are called the multiplicative operators.

MultiplicativeExpression:
   UnaryExpression
   MultiplicativeExpression * UnaryExpression
   MultiplicativeExpression / UnaryExpression
   MultiplicativeExpression % UnaryExpression

The multiplicative operators have the same precedence and are syntactically leftassociative (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.

15.17.1 Multiplication Operator *

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 either operand is NaN, the result is NaN.

• 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 zero results in NaN.

• 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 type of the multiplication expression is float, then the float value set must be chosen.

If the type of the multiplication expression is double, then the double value set must be chosen.

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

15.17.2 Division Operator /

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 either operand is NaN, the result is NaN.

• 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 an infinity results in NaN.

• 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 type of the division expression is float, then the float value set must be chosen.

If the type of the division expression is double, then the double value set must be chosen.

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

15.17.3 Remainder Operator %

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

Click here to view code image

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:

Click here to view code image

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 either operand is NaN, the result is NaN.

• 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

Click here to view code image

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

15.18 Additive Operators

The operators + and - are called the additive operators.

AdditiveExpression:
    MultiplicativeExpression
    AdditiveExpression + MultiplicativeExpression
    AdditiveExpression - MultiplicativeExpression

The additive operators have the same precedence and are syntactically leftassociative (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.

15.18.1 String Concatenation Operator +

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:

Click here to view code image

"The square root of 2 is " + Math.sqrt(2)

produces the result:

Click here to view code image

"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:

Click here to view code image

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:

Click here to view code image

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:

Click here to view code image

"You take one down and pass it around:"

into two pieces to avoid an inconveniently long line in the source code.

15.18.2 Additive Operators (+ and -) for Numeric Types

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:

• If either operand is NaN, the result is NaN.

• The sum of two infinities of opposite sign is NaN.

• 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 opposite sign is positive zero.

• 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 type of the addition expression is float, then the float value set must be chosen.

If the type of the addition expression is double, then the double value set must be chosen.

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

15.19 Shift Operators

The operators << (left shift), >> (signed right shift), and >>> (unsigned right shift) are called the shift operators. The left-hand operand of a shift operator is the value to be shifted; the right-hand operand specifies the shift distance.

ShiftExpression:
   AdditiveExpression
   ShiftExpression << AdditiveExpression
   ShiftExpression >> AdditiveExpression
   ShiftExpression >>> AdditiveExpression

The shift operators are syntactically left-associative (they group left-to-right).

Unary numeric promotion (§5.6.1) is performed on each operand separately. (Binary numeric promotion (§5.6.2) is not performed on the operands.)

It is a compile-time error if the type of each of the operands of a shift operator, after unary numeric promotion, is not a primitive integral type.

The type of the shift expression is the promoted type of the left-hand operand.

If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator &15.22.1) with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.

If the promoted type of the left-hand operand is long, then only the six lowestorder 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.

15.20 Relational Operators

The numerical comparison operators <, >, <=, and >=, and the instanceof operator, are called the relational operators.

RelationalExpression:
   ShiftExpression
   RelationalExpression < ShiftExpression
   RelationalExpression > ShiftExpression
   RelationalExpression <= ShiftExpression
   RelationalExpression >= ShiftExpression
   RelationalExpression instanceof ReferenceType

The relational operators are syntactically left-associative (they group left-to-right).

However, this fact is not useful. For example, a<b<c parses as (a<b)<c, which is always a compile-time error, because the type of a<b is always boolean and < is not an operator on boolean values.

The type of a relational expression is always boolean.

15.20.1 Numerical Comparison Operators <, <=, >, and >=

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:

• If either operand is NaN, then the result is false.

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

15.20.2 Type Comparison Operator instanceof

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

Click here to view code image

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):

Click here to view code image

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.

15.21 Equality Operators

The operators == (equal to) and != (not equal to) are called the equality operators.

EqualityExpression:
    RelationalExpression
    EqualityExpression == RelationalExpression
    EqualityExpression != RelationalExpression

The equality operators are syntactically left-associative (they group left-to-right).

However, this fact is essentially never useful. For example, a==b==c parses as (a==b)==c. The result type of a==b is always boolean, and c must therefore be of type boolean or a compile-time error occurs. Thus, a==b==c does not test to see whether a, b, and c are all equal.

The equality operators are commutative if the operand expressions have no side effects.

The equality operators are analogous to the relational operators except for their lower precedence. Thus, a<b==c<d is true whenever a<b and c<d have the same truth value.

The equality operators may be used to compare two operands that are convertible (§5.1.8) to numeric type, or two operands of type boolean or Boolean, or two operands that are each of either reference type or the null type. All other cases result in a compile-time error.

The type of an equality expression is always boolean.

In all cases, a!=b produces the same result as !(a==b).

15.21.1 Numerical Equality Operators == and !=

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.

15.21.2 Boolean Equality Operators == and !=

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.

15.21.3 Reference Equality Operators == and !=

If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.

It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion (§5.5). The run-time values of the two operands would necessarily be unequal.

At run time, the result of == is true if the operand values are both null or both refer to the same object or array; otherwise, the result is false.

The result of != is false if the operand values are both null or both refer to the same object or array; otherwise, the result is true.

While == may be used to compare references of type String, such an equality test determines whether or not the two operands refer to the same String object. The result is false if the operands are distinct String objects, even if they contain the same sequence of characters (§3.10.5). The contents of two strings s and t can be tested for equality by the method invocation s.equals(t).

15.22 Bitwise and Logical Operators

The bitwise operators and logical operators include the AND operator &, exclusive OR operator ^, and inclusive OR operator |.

AndExpression:
    EqualityExpression
    AndExpression & EqualityExpression

ExclusiveOrExpression:
    AndExpression
    ExclusiveOrExpression ^ AndExpression

InclusiveOrExpression:
    ExclusiveOrExpression
    InclusiveOrExpression | ExclusiveOrExpression

These operators have different precedence, with & having the highest precedence and | the lowest precedence.

Each of these operators is syntactically left-associative (each groups left-to-right).

Each operator is commutative if the operand expressions have no side effects.

Each operator is associative.

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.

15.22.1 Integer Bitwise Operators &, ^, and |

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

15.22.2 Boolean Logical Operators &, ^, and |

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.

15.23 Conditional-And Operator &&

The conditional-and operator && is like &15.22.2), but evaluates its right-hand operand only if the value of its left-hand operand is true.

ConditionalAndExpression:
    InclusiveOrExpression
    ConditionalAndExpression && InclusiveOrExpression

The conditional-and operator is syntactically left-associative (it groups left-to-right).

The conditional-and operator is fully associative with respect to both side effects and result value. That is, for any expressions a, b, and c, evaluation of the expression ((a) && (b)) && (c) produces the same result, with the same side effects occurring in the same order, as evaluation of the expression (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.

15.24 Conditional-Or Operator ||

The conditional-or operator || operator is like |15.22.2), but evaluates its righthand operand only if the value of its left-hand operand is false.

ConditionalOrExpression:
    ConditionalAndExpression
    ConditionalOrExpression || ConditionalAndExpression

The conditional-or operator is syntactically left-associative (it groups left-to-right).

The conditional-or operator is fully associative with respect to both side effects and result value. That is, for any expressions a, b, and c, evaluation of the expression ((a) || (b)) || (c) produces the same result, with the same side effects occurring in the same order, as evaluation of the expression (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.

15.25 Conditional Operator ? :

The conditional operator ? : uses the boolean value of one expression to decide which of two other expressions should be evaluated.

ConditionalExpression:
    ConditionalOrExpression
    ConditionalOrExpression ? Expression : ConditionalExpression
    ConditionalOrExpression ? Expression : LambdaExpression

The conditional operator is syntactically right-associative (it groups right-to-left). Thus, a?b:c?d:e?f:g means the same as a?b:(c?d:(e?f:g)).

The conditional operator has three operand expressions. ? appears between the first and second expressions, and : appears between the second and third expressions.

The first expression must be of type boolean or Boolean, or a compile-time error occurs.

It is a compile-time error for either the second or the third operand expression to be an invocation of a void method.

In fact, by the grammar of expression statements (§14.8), it is not permitted for a conditional expression to appear in any context where an invocation of a void method could appear.

There are three kinds of conditional expressions, classified according to the second and third operand expressions: boolean conditional expressions, numeric conditional expressions, and reference conditional expressions. The classification rules are as follows:

• If both the second and the third operand expressions are boolean expressions, the conditional expression is a boolean conditional expression.

For the purpose of classifying a conditional, the following expressions are boolean expressions:

– An expression of a standalone form (§15.2) that has type boolean or Boolean.

– A parenthesized boolean expression (§15.8.5).

– A class instance creation expression (§15.9) for class Boolean.

– A method invocation expression (§15.12) for which the chosen most specific method (§15.12.2.5) has return type boolean or Boolean.

Note that, for a generic method, this is the type before instantiating the method’s type arguments.

– A boolean conditional expression.

• If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.

For the purpose of classifying a conditional, the following expressions are numeric expressions:

– An expression of a standalone form (§15.2) with a type that is convertible to a numeric type (§4.2, §5.1.8).

– A parenthesized numeric expression (§15.8.5).

– A class instance creation expression (§15.9) for a class that is convertible to a numeric type.

– A method invocation expression (§15.12) for which the chosen most specific method (§15.12.2.5) has a return type that is convertible to a numeric type.

– A numeric conditional expression.

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

Image

Table 15.25-A. Conditional expression type (Primitive 3rd operand, Part I)

Image

Table 15.25-B. Conditional expression type (Primitive 3rd operand, Part II)

Image

Table 15.25-C. Conditional expression type (Reference 3rd operand, Part I)

Image

Table 15.25-D. Conditional expression type (Reference 3rd operand, Part II)

Image

Table 15.25-E. Conditional expression type (Reference 3rd operand, Part III)

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:

• If the value of the first operand is true, then the second operand expression is chosen.

• If the value of the first operand is false, then the third operand expression is chosen.

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.

15.25.1 Boolean Conditional Expressions

Boolean conditional expressions are standalone expressions (§15.2).

The type of a boolean conditional expression is determined as follows:

• If the second and third operands are both of type Boolean, the conditional expression has type Boolean.

• Otherwise, the conditional expression has type boolean.

15.25.2 Numeric Conditional Expressions

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

15.25.3 Reference Conditional Expressions

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:

Click here to view code image

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:

Click here to view code image

List<String> ls = Arrays.asList();

but this was not:

Click here to view code image

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>:

Click here to view code image

Class<? super Integer> choose(boolean b,
                              Class<Integer> c1,
                              Class<Number> c2) {
    return b ? c1 : c2;
}

If the conditional expression was not a poly expression, then a compile-time error would occur, as its type would be lub(Class<Integer>, Class<Number>) = Class<? extends Number> which is incompatible with the return type of choose.

15.26 Assignment Operators

There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.

AssignmentExpression:
    ConditionalExpression
    Assignment

Assignment:
    LeftHandSide AssignmentOperator Expression

LeftHandSide:
    ExpressionName
    FieldAccess
    ArrayAccess

AssignmentOperator: one of
    =  *=  /=  %=  +=  -=  <<=  >>=  >>>=  &=  ^=  |=

The result of the first operand of an assignment operator must be a variable, or a compile-time error occurs.

This operand may be a named variable, such as a local variable or a field of the current object or class, or it may be a computed variable, as can result from a field access (§15.11) or an array access (§15.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.

15.26.1 Simple 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.f15.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 righthand 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 lefthand 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

Click here to view code image

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:

Click here to view code image

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:

Click here to view code image

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.

15.26.2 Compound Assignment Operators

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 righthand 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

Click here to view code image

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:

Click here to view code image

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:

Click here to view code image

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

Click here to view code image

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:

Click here to view code image

k += (k = 4) * (k + 2);
a[0] += (a[0] = 4) * (a[0] + 2);

behave in exactly the same manner as the statements:

Click here to view code image

k = k + (k = 4) * (k + 2);
a[0] = a[0] + (a[0] = 4) * (a[0] + 2);

15.27 Lambda Expressions

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.

LambdaExpression:
    LambdaParameters -> LambdaBody

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:

Click here to view code image

() -> {}                // 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 had an ambiguity 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 (annotations, final) are unique to parameter lists, while 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 ambiguity cannot be resolved 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.

15.27.1 Lambda Parameters

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.

LambdaParameters:
    Identifier
    ( [FormalParameterList] )
    ( InferredFormalParameterList )

InferredFormalParameterList:
    Identifier {, Identifier}

The following productions from §4.3, §8.3, and §8.4.1 are shown here for convenience:

FormalParameterList:
     FormalParameters , LastFormalParameter
     LastFormalParameter

FormalParameters:
     FormalParameter {, FormalParameter}
     ReceiverParameter {, FormalParameter}

FormalParameter:
     {VariableModifier} UnannType VariableDeclaratorId

LastFormalParameter:
     {VariableModifier} UnannType {Annotation} ... VariableDeclaratorId
     FormalParameter

VariableModifier: one of
     Annotation final

VariableDeclaratorId:
     Identifier [Dims]

Dims:
     {Annotation} [ ] {{Annotation} [ ]}

Receiver parameters are not permitted in the FormalParameters of a lambda expression, as specified in §8.4.1.

A lambda expression whose formal parameters have declared types is said to be explicitly typed, while a lambda expression whose formal parameters have inferred types is said to be implicitly typed. 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 is denoted by the UnannType that appears in its parameter specifier, followed by any bracket pairs that follow the Identifier in the declarator, except for a variable arity parameter, whose declared type is an array type whose component type is the UnannType that appears in its parameter specifier.

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.

15.27.2 Lambda Body

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.

LambdaBody:
    Expression
    Block

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:

Click here to view code image

() -> {}
() -> { System.out.println("done"); }

These are value-compatible:

Click here to view code image

() -> { return "done"; }
() -> { if (...) return 1; else return 0; }

These are both:

Click here to view code image

() -> { throw new RuntimeException(); }
() -> { while (true); }

This is neither:

Click here to view code image

() -> { if (...) return "done"; System.out.println("done"); }

The handling of void/value-compatible and the meaning of names in the body jointly serve to minimize the dependency on a particular target type in the given context, which is useful both for implementations and for programmer comprehension. While expressions can be assigned different types during overload resolution depending on the target type, the meaning of unqualified names and the basic structure of the lambda body do not change.

Note that the void/value-compatible definition is not a strictly structural property: “can complete normally” depends on the values of constant expressions, and these may include names that reference constant variables.

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.

Any local variable used but not declared in a lambda body must be definitely assigned (§16 (Definite Assignment)) before the lambda body, or a compile-time error occurs.

Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.

The restriction to effectively final variables includes standard loop variables, but not enhanced-for loop variables, which are treated as distinct for each iteration of the loop (§14.14.2).

The following lambda bodies demonstrate use of effectively final variables.

Click here to view code image

void m1(int x) {
    int y = 1;
    foo(() -> x+y);
    // Legal: x and y are both effectively final.
}

void m2(int x) {
    int y;
    y = 1;
    foo(() -> x+y);
    // Legal: x and y are both effectively final.
}

void m3(int x) {
    int y;
    if (...) y = 1;
    foo(() -> x+y);
    // Illegal: y is effectively final, but not definitely assigned.
}

void m4(int x) {
    int y;
    if (...) y = 1; else y = 2;
    foo(() ->; x+y);
    // Legal: x and y are both effectively final.
}

void m5(int x) {
    int y;
    if (...) y = 1;
    y = 2;
    foo(() -> x+y);
    // Illegal: y is not effectively final.
}

void m6(int x) {
    foo(() -> x+1);
    x++;
    // Illegal: x is not effectively final.
}

void m7(int x) {
    foo(() -> x=1);
    // Illegal: x is not effectively final.
}

void m8() {
    int y;
    foo(() -> y=1);
    // Illegal: y is not definitely assigned before the lambda.
}

void m9(String[] arr) {
    for (String s : arr) {
    foo(() -> s);
        // Legal: s is effectively final
        // (it is a new variable on each iteration)
    }
}

void m10(String[] arr) {
    for (int i = 0; i < arr.length; i++) {
        foo(() -> arr[i]);
        // Illegal: i is not effectively final
        // (it is not final, and is incremented)
    }
}

15.27.3 Type of a Lambda Expression

A lambda expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.

The ground target type is derived from T as follows:

• If T is a wildcard-parameterized functional interface type and the lambda expression is explicitly typed, then the ground target type is inferred as described in §18.5.3.

• If T is a wildcard-parameterized functional interface type and the lambda expression is implicitly typed, then the ground target type is the non-wildcard parameterization (§9.9) of T.

• Otherwise, the ground target type is T.

A lambda expression is congruent with a function type if all of the following are true:

• The function type has no type parameters.

• The number of lambda parameters is the same as the number of parameter types of the function type.

• If the lambda expression is explicitly typed, its formal parameter types are the same as the parameter types of the function type.

• If the lambda parameters are assumed to have the same types as the function type’s parameter types, then:

– If the function type’s result is void, the lambda body is either a statement expression or a void-compatible block.

– If the function type’s result is a (non-void) type R, then either i) the lambda body is an expression that is compatible with R in an assignment context, or ii) the lambda body is a value-compatible block, and each result expression (§15.27.2) is compatible with R in an assignment context.

If a lambda expression is compatible with a target type T, then the type of the expression, U, is the ground target type derived from T.

It is a compile-time error if any class or interface mentioned by either U or the function type of U is not accessible from the class or interface in which the lambda expression appears.

For each non-static member method m of U, if the function type of U has a subsignature of the signature of m, then a notional method whose method type is the function type of U is deemed to override m, and any compile-time error or unchecked warning specified in §8.4.8.3 may occur.

A checked exception that can be thrown in the body of the lambda expression may cause a compile-time error, as specified in §11.2.3.

The parameter types of explicitly typed lambdas are required to exactly match those of the function type. While it would be possible to be more flexible - allow boxing or contravariance, for example - this kind of generality seems unnecessary, and is inconsistent with the way overriding works in class declarations. A programmer ought to know exactly what function type is being targeted when writing a lambda expression, so he should thus know exactly what signature must be overridden. (In contrast, this is not the case for method references, and so more flexibility is allowed when they are used.) In addition, more flexibility with parameter types would add to the complexity of type inference and overload resolution.

Note that while boxing is not allowed in a strict invocation context, boxing of lambda result expressions is always allowed - that is, the result expression appears in an assignment context, regardless of the context enclosing the lambda expression. However, if an explicitly typed lambda expression is an argument to an overloaded method, a method signature that avoids boxing or unboxing the lambda result is preferred by the most specific check (§15.12.2.5).

If the body of a lambda is a statement expression (that is, an expression that would be allowed to stand alone as a statement), it is compatible with a void-producing function type; any result is simply discarded. So, for example, both of the following are legal:

Click here to view code image

// Predicate has a boolean result
java.util.function.Predicate<String> p = s -> list.add(s);
// Consumer has a void result
java.util.function.Consumer<String> c = s -> list.add(s);

Generally speaking, a lambda of the form () -> expr, where expr is a statement expression, is interpreted as either () -> { return expr; } or () -> { expr; }, depending on the target type.

15.27.4 Run-Time Evaluation of Lambda Expressions

At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a lambda expression is distinct from execution of the lambda body.

Either a new instance of a class with the properties below is allocated and initialized, or an existing instance of a class with the properties below is referenced. If a new instance is to be created, but there is insufficient space to allocate the object, evaluation of the lambda expression completes abruptly by throwing an OutOfMemoryError.

The value of a lambda expression is a reference to an instance of a class with the following properties:

• The class implements the targeted functional interface type and, if the target type is an intersection type, every other interface type mentioned in the intersection.

• Where the lambda expression has type U, for each non-static member method m of U:

If the function type of U has a subsignature of the signature of m, then the class declares a method that overrides m. The method’s body has the effect of evaluating the lambda body, if it is an expression, or of executing the lambda body, if it is a block; if a result is expected, it is returned from the method.

If the erasure of the type of a method being overridden differs in its signature from the erasure of the function type of U, then before evaluating or executing the lambda body, the method’s body checks that each argument value is an instance of a subclass or subinterface of the erasure of the corresponding parameter type in the function type of U; if not, a ClassCastException is thrown.

• The class overrides no other methods of the targeted functional interface type or other interface types mentioned above, although it may override methods of the Object class.

These rules are meant to offer flexibility to implementations of the Java programming language, in that:

• A new object need not be allocated on every evaluation.

• Objects produced by different lambda expressions need not belong to different classes (if the bodies are identical, for example).

• Every object produced by evaluation need not belong to the same class (captured local variables might be inlined, for example).

• If an “existing instance” is available, it need not have been created at a previous lambda evaluation (it might have been allocated during the enclosing class’s initialization, for example).

If the targeted functional interface type is a subtype of java.io.Serializable, the resulting object will automatically be an instance of a serializable class. Making an object derived from a lambda expression serializable can have extra run time overhead and security implications, so lambda-derived objects are not required to be serializable “by default”.

15.28 Constant Expressions

ConstantExpression:
    Expression

A constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following:

• Literals of primitive type and literals of type String3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)

• Casts to primitive types and casts to type String15.16)

• The unary operators +, -, ~, and ! (but not ++ or --) (§15.15.3, §15.15.4, §15.15.5, §15.15.6)

• The multiplicative operators *, /, and %15.17)

• The additive operators + and -15.18)

• The shift operators <<, >>, and >>>15.19)

• The relational operators <, <=, >, and >= (but not instanceof) (§15.20)

• The equality operators == and !=15.21)

• The bitwise and logical operators &, ^, and |15.22)

• The conditional-and operator && and the conditional-or operator ||15.23, §15.24)

• The ternary conditional operator ? :15.25)

• Parenthesized expressions (§15.8.5) whose contained expression is a constant expression.

• Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).

• Qualified names (§6.5.6.2) of the form TypeName . Identifier that refer to constant variables (§4.12.4).

Constant expressions of type String are always “interned” so as to share unique instances, using the method String.intern.

A constant expression is always treated as FP-strict (§15.4), even if it occurs in a context where a non-constant expression would not be considered to be FP-strict.

Constant expressions are used as case labels in switch statements (§14.11) and have a special significance for assignment conversion (§5.2) and initialization of a class or interface (§12.4.2). They may also govern the ability of a while, do, or for statement to complete normally (§14.21), and the type of a conditional operator ? : with numeric operands.

Example 15.28-1. Constant Expressions

Click here to view code image

true
(short)(1*2*3*4*5*6)
Integer.MAX_VALUE / 2
2.0 * Math.PI
"The integer " + Long.MAX_VALUE + " is mighty big."

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset