Nested Class Assignment Operator Definition

Table of Contents

15.1. Evaluation, Denotation, and Result
15.2. Forms of Expressions
15.3. Type of an Expression
15.4. FP-strict Expressions
15.5. Expressions and Run-Time Checks
15.6. Normal and Abrupt Completion of Evaluation
15.7. Evaluation Order
15.7.1. Evaluate Left-Hand Operand First
15.7.2. Evaluate Operands before Operation
15.7.3. Evaluation Respects Parentheses and Precedence
15.7.4. Argument Lists are Evaluated Left-to-Right
15.7.5. Evaluation Order for Other Expressions
15.8. Primary Expressions
15.8.1. Lexical Literals
15.8.2. Class Literals
15.8.3.
15.8.4. Qualified
15.8.5. Parenthesized Expressions
15.9. Class Instance Creation Expressions
15.9.1. Determining the Class being Instantiated
15.9.2. Determining Enclosing Instances
15.9.3. Choosing the Constructor and its Arguments
15.9.4. Run-Time Evaluation of Class Instance Creation Expressions
15.9.5. Anonymous Class Declarations
15.9.5.1. Anonymous Constructors
15.10. Array Creation and Access Expressions
15.10.1. Array Creation Expressions
15.10.2. Run-Time Evaluation of Array Creation Expressions
15.10.3. Array Access Expressions
15.10.4. Run-Time Evaluation of Array Access Expressions
15.11. Field Access Expressions
15.11.1. Field Access Using a Primary
15.11.2. Accessing Superclass Members using
15.12. Method Invocation Expressions
15.12.1. Compile-Time Step 1: Determine Class or Interface to Search
15.12.2. Compile-Time Step 2: Determine Method Signature
15.12.2.1. Identify Potentially Applicable Methods
15.12.2.2. Phase 1: Identify Matching Arity Methods Applicable by Strict Invocation
15.12.2.3. Phase 2: Identify Matching Arity Methods Applicable by Loose Invocation
15.12.2.4. Phase 3: Identify Methods Applicable by Variable Arity Invocation
15.12.2.5. Choosing the Most Specific Method
15.12.2.6. Method Invocation Type
15.12.3. Compile-Time Step 3: Is the Chosen Method Appropriate?
15.12.4. Run-Time Evaluation of Method Invocation
15.12.4.1. Compute Target Reference (If Necessary)
15.12.4.2. Evaluate Arguments
15.12.4.3. Check Accessibility of Type and Method
15.12.4.4. Locate Method to Invoke
15.12.4.5. Create Frame, Synchronize, Transfer Control
15.13. Method Reference Expressions
15.13.1. Compile-Time Declaration of a Method Reference
15.13.2. Type of a Method Reference
15.13.3. Run-Time Evaluation of Method References
15.14. Postfix Expressions
15.14.1. Expression Names
15.14.2. Postfix Increment Operator
15.14.3. Postfix Decrement Operator
15.15. Unary Operators
15.15.1. Prefix Increment Operator
15.15.2. Prefix Decrement Operator
15.15.3. Unary Plus Operator
15.15.4. Unary Minus Operator
15.15.5. Bitwise Complement Operator
15.15.6. Logical Complement Operator
15.16. Cast Expressions
15.17. Multiplicative Operators
15.17.1. Multiplication Operator
15.17.2. Division Operator
15.17.3. Remainder Operator
15.18. Additive Operators
15.18.1. String Concatenation Operator
15.18.2. Additive Operators ( and ) for Numeric Types
15.19. Shift Operators
15.20. Relational Operators
15.20.1. Numerical Comparison Operators , , , and
15.20.2. Type Comparison Operator
15.21. Equality Operators
15.21.1. Numerical Equality Operators and
15.21.2. Boolean Equality Operators and
15.21.3. Reference Equality Operators and
15.22. Bitwise and Logical Operators
15.22.1. Integer Bitwise Operators , , and
15.22.2. Boolean Logical Operators , , and
15.23. Conditional-And Operator
15.24. Conditional-Or Operator
15.25. Conditional Operator
15.25.1. Boolean Conditional Expressions
15.25.2. Numeric Conditional Expressions
15.25.3. Reference Conditional Expressions
15.26. Assignment Operators
15.26.1. Simple Assignment Operator
15.26.2. Compound Assignment Operators
15.27. Lambda Expressions
15.27.1. Lambda Parameters
15.27.2. Lambda Body
15.27.3. Type of a Lambda Expression
15.27.4. Run-Time Evaluation of Lambda Expressions
15.28. Constant Expressions

Much of the work in a program is done by evaluating expressions, either for their side effects, such as assignments to variables, or for their values, which can be used as arguments or operands in larger expressions, or to affect the execution sequence in statements, or both.

This chapter specifies the meanings of expressions and the rules for their evaluation.

When an expression in a program is evaluated (executed), the result denotes one of three things:

If an expression denotes a variable, and a value is required for use in further evaluation, then the value of that variable is used. In this context, if the expression denotes a variable or a value, we may speak simply of the value of the expression.

Value set conversion (§5.1.13) is applied to the result of every expression that produces a value, including when the value of a variable of type or is used.

An expression denotes nothing if and only if it is a method invocation (§15.12) that invokes a method that does not return a value, that is, a method declared (§8.4). Such an expression can be used only as an expression statement (§14.8) or as the single expression of a lambda body (§15.27.2), because every other context in which an expression can appear requires the expression to denote something. An expression statement or lambda body that is a method invocation may also invoke a method that produces a result; in this case the value returned by the method is quietly discarded.

Evaluation of an expression can produce side effects, because expressions may contain embedded assignments, increment operators, decrement operators, and method invocations.

An expression occurs in either:

  • The declaration of some (class or interface) type that is being declared: in a field initializer, in a static initializer, in an instance initializer, in a constructor declaration, in a method declaration, or in an annotation.

  • An annotation on a package declaration or on a top level type declaration.

15.1. Evaluation, Denotation, and Result

Expressions can be broadly categorized into one of the following syntactic forms:

Precedence among operators is managed by a hierarchy of grammar productions. The lowest precedence operator is the arrow of a lambda expression (), followed by the assignment operators. Thus, all expressions are syntactically included in the LambdaExpression and AssignmentExpression nonterminals:

When some expressions appear in certain contexts, they are considered poly expressions. The following forms of expressions may be poly expressions:

The rules determining whether an expression of one of these forms is a poly expression are given in the individual sections that specify these forms of expressions.

Expressions that are not poly expressions are standalone expressions. Standalone expressions are expressions of the forms above when determined not to be poly expressions, as well as all expressions of all other forms. Expressions of all other forms are said to have a standalone form.

Some expressions have a value that can be determined at compile time. These are constant expressions (§15.28).

15.2. Forms of Expressions

If an expression denotes a variable or a value, then the expression has a type known at compile time. The type of a standalone expression can be determined entirely from the contents of the expression; in contrast, the type of a poly expression may be influenced by the expression's target type (§5 (Conversions and Contexts)). The rules for determining the type of an expression are explained separately below for each kind of expression.

The value of an expression is assignment compatible (§5.2) with the type of the expression, unless heap pollution occurs (§4.12.2).

Likewise, the value stored in a variable is always compatible with the type of the variable, unless heap pollution occurs.

In other words, the value of an expression whose type is T is always suitable for assignment to a variable of type T.

Note that an expression whose type is a class type F that is declared is guaranteed to have a value that is either a null reference or an object whose class is F itself, because types have no subclasses.

15.3. Type of an Expression

If the type of an expression is or , then there is a question as to what value set (§4.2.3) the value of the expression is drawn from. This is governed by the rules of value set conversion (§5.1.13); these rules in turn depend on whether or not the expression is FP-strict.

Every constant expression (§15.28) is FP-strict.

If an expression is not a constant expression, then consider all the class declarations, interface declarations, and method declarations that contain the expression. If any such declaration bears the modifier (§8.1.1.3, §8.4.3.5, §9.1.1.2), then the expression is FP-strict.

If a class, interface, or method, X, is declared , then X and any class, interface, method, constructor, instance initializer, static initializer, or variable initializer within X is said to be FP-strict.

Note that an annotation's element value (§9.7) is always FP-strict, because it is always a constant expression.

It follows that an expression is not FP-strict if and only if it is not a constant expression and it does not appear within any declaration that has the modifier.

Within an FP-strict expression, all intermediate values must be elements of the float value set or the double value set, implying that the results of all FP-strict expressions must be those predicted by IEEE 754 arithmetic on operands represented using single and double formats.

Within an expression that is not FP-strict, some leeway is granted for an implementation to use an extended exponent range to represent intermediate results; the net effect, roughly speaking, is that a calculation might produce "the correct answer" in situations where exclusive use of the float value set or double value set might result in overflow or underflow.

15.4. FP-strict Expressions

If the type of an expression is a primitive type, then the value of the expression is of that same primitive type.

If the type of an expression is a reference type, then the class of the referenced object, or even whether the value is a reference to an object rather than , is not necessarily known at compile time. There are a few places in the Java programming language where the actual class of a referenced object affects program execution in a manner that cannot be deduced from the type of the expression. They are as follows:

  • Method invocation (§15.12). The particular method used for an invocation is chosen based on the methods that are part of the class or interface that is the type of . For instance methods, the class of the object referenced by the run-time value of participates because a subclass may override a specific method already declared in a parent class so that this overriding method is invoked. (The overriding method may or may not choose to further invoke the original overridden method.)

  • The operator (§15.20.2). An expression whose type is a reference type may be tested using to find out whether the class of the object referenced by the run-time value of the expression is assignment compatible (§5.2) with some other reference type.

  • Casting (§5.5, §15.16). The class of the object referenced by the run-time value of the operand expression might not be compatible with the type specified by the cast. For reference types, this may require a run-time check that throws an exception if the class of the referenced object, as determined at run time, is not assignment compatible (§5.2) with the target type.

  • Assignment to an array component of reference type (§10.5, §15.13, §15.26.1). The type-checking rules allow the array type S to be treated as a subtype of T if S is a subtype of T, but this requires a run-time check for assignment to an array component, similar to the check performed for a cast.

  • Exception handling (§14.20). An exception is caught by a clause only if the class of the thrown exception object is an the type of the formal parameter of the clause.

Situations where the class of an object is not statically known may lead to run-time type errors.

In addition, there are situations where the statically known type may not be accurate at run time. Such situations can arise in a program that gives rise to compile-time unchecked warnings. Such warnings are given in response to operations that cannot be statically guaranteed to be safe, and cannot immediately be subjected to dynamic checking because they involve non-reifiable types (§4.7). As a result, dynamic checks later in the course of program execution may detect inconsistencies and result in run-time type errors.

A run-time type error can occur only in these situations:

  • In a cast, when the actual class of the object referenced by the value of the operand expression is not compatible with the target type specified by the cast operator (§5.5, §15.16); in this case a is thrown.

  • In an automatically generated cast introduced to ensure the validity of an operation on a non-reifiable type (§4.7).

  • In an assignment to an array component of reference type, when the actual class of the object referenced by the value to be assigned is not compatible with the actual run-time component type of the array (§10.5, §15.13, §15.26.1); in this case an is thrown.

  • When an exception is not caught by any clause of a statement (§14.20); in this case the thread of control that encountered the exception first attempts to invoke an uncaught exception handler (§11.3) and then terminates.

15.5. Expressions and Run-Time Checks

Every expression has a normal mode of evaluation in which certain computational steps are carried out. The following sections describe the normal mode of evaluation for each kind of expression.

If all the steps are carried out without an exception being thrown, the expression is said to complete normally.

If, however, evaluation of an expression throws an exception, then the expression is said to complete abruptly. An abrupt completion always has an associated reason, which is always a with a given value.

Run-time exceptions are thrown by the predefined operators as follows:

  • A class instance creation expression (§15.9.4), array creation expression (§15.10.2), method reference expression (§15.13.3), array initializer expression (§10.6), string concatenation operator expression (§15.18.1), or lambda expression (§15.27.4) throws an if there is insufficient memory available.

  • An array creation expression (§15.10.2) throws a if the value of any dimension expression is less than zero.

  • An array access expression (§15.10.4) throws a if the value of the array reference expression is .

  • An array access expression (§15.10.4) throws an if the value of the array index expression is negative or greater than or equal to the of the array.

  • A field access expression (§15.11) throws a if the value of the object reference expression is .

  • A method invocation expression (§15.12) that invokes an instance method throws a if the target reference is .

  • A cast expression (§15.16) throws a if a cast is found to be impermissible at run time.

  • An integer division (§15.17.2) or integer remainder (§15.17.3) operator throws an if the value of the right-hand operand expression is zero.

  • An assignment to an array component of reference type (§15.26.1), a method invocation expression (§15.12), or a prefix or postfix increment (§15.14.2, §15.15.1) or decrement operator (§15.14.3, §15.15.2) may all throw an as a result of boxing conversion (§5.1.7).

  • An assignment to an array component of reference type (§15.26.1) throws an when the value to be assigned is not compatible with the component type of the array (§10.5).

A method invocation expression can also result in an exception being thrown if an exception occurs that causes execution of the method body to complete abruptly.

A class instance creation expression can also result in an exception being thrown if an exception occurs that causes execution of the constructor to complete abruptly.

Various linkage and virtual machine errors may also occur during the evaluation of an expression. By their nature, such errors are difficult to predict and difficult to handle.

If an exception occurs, then evaluation of one or more expressions may be terminated before all steps of their normal mode of evaluation are complete; such expressions are said to complete abruptly.

If evaluation of an expression requires evaluation of a subexpression, then abrupt completion of the subexpression always causes the immediate abrupt completion of the expression itself, with the same reason, and all succeeding steps in the normal mode of evaluation are not performed.

The terms "complete normally" and "complete abruptly" are also applied to the execution of statements (§14.1). A statement may complete abruptly for a variety of reasons, not just because an exception is thrown.

15.6. Normal and Abrupt Completion of Evaluation

The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.

It is recommended that code not rely crucially on this specification. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.

The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.

If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.

If evaluation of the left-hand operand of a binary operator completes abruptly, no part of the right-hand operand appears to have been evaluated.

Example 15.7.1-1. Left-Hand Operand Is Evaluated First

In the following program, the operator has a left-hand operand that contains an assignment to a variable and a right-hand operand that contains a reference to the same variable. The value produced by the reference will reflect the fact that the assignment occurred first.

class Test1 { public static void main(String[] args) { int i = 2; int j = (i=3) * i; System.out.println(j); } }

This program produces the output:

9

It is not permitted for evaluation of the operator to produce instead of .


Example 15.7.1-2. Implicit Left-Hand Operand In Operator Of Compound Assigment

In the following program, the two assignment statements both fetch and remember the value of the left-hand operand, which is , before the right-hand operand of the addition operator is evaluated, at which point the variable is set to .

class Test2 { public static void main(String[] args) { int a = 9; a += (a = 3); // first example System.out.println(a); int b = 9; b = b + (b = 3); // second example System.out.println(b); } }

This program produces the output:

12 12

It is not permitted for either assignment (compound for , simple for ) to produce the result .

See also the example in §15.26.2.


Example 15.7.1-3. Abrupt Completion of Evaluation of the Left-Hand Operand

class Test3 { public static void main(String[] args) { int j = 1; try { int i = forgetIt() / (j = 2); } catch (Exception e) { System.out.println(e); System.out.println("Now j = " + j); } } static int forgetIt() throws Exception { throw new Exception("I'm outta here!"); } }

This program produces the output:

java.lang.Exception: I'm outta here! Now j = 1

That is, the left-hand operand of the operator throws an exception before the right-hand operand is evaluated and its embedded assignment of to occurs.


15.7.1. Evaluate Left-Hand Operand First

The Java programming language guarantees that every operand of an operator (except the conditional operators , , and ) appears to be fully evaluated before any part of the operation itself is performed.

If the binary operator is an integer division (§15.17.2) or integer remainder (§15.17.3), then its execution may raise an , but this exception is thrown only after both operands of the binary operator have been evaluated and only if these evaluations completed normally.

Example 15.7.2-1. Evaluation of Operands Before Operation

class Test { public static void main(String[] args) { int divisor = 0; try { int i = 1 / (divisor * loseBig()); } catch (Exception e) { System.out.println(e); } } static int loseBig() throws Exception { throw new Exception("Shuffle off to Buffalo!"); } }

This program produces the output:

java.lang.Exception: Shuffle off to Buffalo!

and not:

java.lang.ArithmeticException: / by zero

since no part of the division operation, including signaling of a divide-by-zero exception, may appear to occur before the invocation of completes, even though the implementation may be able to detect or infer that the division operation would certainly result in a divide-by-zero exception.


15.7.2. Evaluate Operands before Operation

The Java programming language respects the order of evaluation indicated explicitly by parentheses and implicitly by operator precedence.

An implementation of the Java programming language may not take advantage of algebraic identities such as the associative law to rewrite expressions into a more convenient computational order unless it can be proven that the replacement expression is equivalent in value and in its observable side effects, even in the presence of multiple threads of execution (using the thread execution model in §17 (Threads and Locks)), for all possible computational values that might be involved.

In the case of floating-point calculations, this rule applies also for infinity and not-a-number (NaN) values.

For example, may not be rewritten as , because these expressions have different values if either or is NaN or both are NaN.

Specifically, floating-point calculations that appear to be mathematically associative are unlikely to be computationally associative. Such computations must not be naively reordered.

For example, it is not correct for a Java compiler to rewrite as ; while roundoff happens not to be an issue here, there are large values of for which the first expression produces infinity (because of overflow) but the second expression produces a finite result.

So, for example, the test program:

strictfp class Test { public static void main(String[] args) { double d = 8e+307; System.out.println(4.0 * d * 0.5); System.out.println(2.0 * d); } }

prints:

Infinity 1.6e+308

because the first expression overflows and the second does not.

In contrast, integer addition and multiplication are provably associative in the Java programming language.

For example , where , , and are local variables (this simplifying assumption avoids issues involving multiple threads and variables), will always produce the same answer whether evaluated as or ; if the expression occurs nearby in the code, a smart Java compiler may be able to use this common subexpression.

15.7.3. Evaluation Respects Parentheses and Precedence

In a method or constructor invocation or class instance creation expression, argument expressions may appear within the parentheses, separated by commas. Each argument expression appears to be fully evaluated before any part of any argument expression to its right.

If evaluation of an argument expression completes abruptly, no part of any argument expression to its right appears to have been evaluated.

Example 15.7.4-1. Evaluation Order At Method Invocation

class Test1 { public static void main(String[] args) { String s = "going, "; print3(s, s, s = "gone"); } static void print3(String a, String b, String c) { System.out.println(a + b + c); } }

This program produces the output:

going, going, gone

because the assignment of the string "" to occurs after the first two arguments to have been evaluated.


Example 15.7.4-2. Abrupt Completion of Argument Expression

class Test2 { static int id; public static void main(String[] args) { try { test(id = 1, oops(), id = 3); } catch (Exception e) { System.out.println(e + ", id=" + id); } } static int test(int a, int b, int c) { return a + b + c; } static int oops() throws Exception { throw new Exception("oops"); } }

This program produces the output:

java.lang.Exception: oops, id=1

because the assignment of to is not executed.


15.7.4. Argument Lists are Evaluated Left-to-Right

The order of evaluation for some expressions is not completely covered by these general rules, because these expressions may raise exceptional conditions at times that must be specified. See the detailed explanations of evaluation order for the following kinds of expressions:

15.7.5. Evaluation Order for Other Expressions

15.7. Evaluation Order

Primary expressions include most of the simplest kinds of expressions, from which all others are constructed: literals, object creations, field accesses, method invocations, method references, and array accesses. A parenthesized expression is also treated syntactically as a primary expression.

This part of the grammar of the Java programming language is unusual, in two ways. First, one might expect simple names, such as names of local variables and method parameters, to be primary expressions. For technical reasons, names are grouped together with primary expressions a little later when postfix expressions are introduced (§15.14).

The technical reasons have to do with allowing left-to-right parsing of Java programs with only one-token lookahead. Consider the expressions and . The first is a parenthesized array access (§15.10.3) and the second is the start of a cast (§15.16). At the point that the look-ahead symbol is , a left-to-right parse will have reduced the to the nonterminal Name. In the context of a cast we prefer not to have to reduce the name to a Primary, but if Name were one of the alternatives for Primary, then we could not tell whether to do the reduction (that is, we could not determine whether the current situation would turn out to be a parenthesized array access or a cast) without looking ahead two tokens, to the token following the . The grammar presented here avoids the problem by keeping Name and Primary separate and allowing either in certain other syntax rules (those for ClassInstanceCreationExpression, MethodInvocation, ArrayAccess, and PostfixExpression, but not for FieldAccess because this uses an identifier directly). This strategy effectively defers the question of whether a Name should be treated as a Primary until more context can be examined.

The second unusual feature avoids a potential grammatical ambiguity in the expression "" which in Java always means a single creation of a multidimensional array, but which, without appropriate grammatical finesse, might also be interpreted as meaning the same as "".

This ambiguity is eliminated by splitting the expected definition of Primary into Primary and PrimaryNoNewArray. (This may be compared to the splitting of Statement into Statement and StatementNoShortIf (§14.5) to avoid the "dangling else" problem.)

A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type , followed by a '' and the token .

The type of C, where C is the name of a class, interface, or array type (§4.3), is C.

The type of p, where p is the name of a primitive type (§4.2), is B, where B is the type of an expression of type p after boxing conversion (§5.1.7).

The type of (§8.4.5) is .

It is a compile-time error if the named type is a type variable (§4.4) or a parameterized type (§4.5) or an array whose element type is a type variable or parameterized type.

It is a compile-time error if the named type does not denote a type that is accessible (§6.6) and in scope (§6.3) at the point where the class literal appears.

A class literal evaluates to the object for the named type (or for ) as defined by the defining class loader (§12.2) of the class of the current instance.

15.8.2. Class Literals

ClassLiteral:

TypeName {}
NumericType {}
{}

The keyword may be used only in the following contexts:

15.8. Primary Expressions

Chapter 15. Expressions


































































      [49]
























      Comments

      Leave a Reply

      Your email address will not be published. Required fields are marked *