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

15.1 Evaluation, Denotation, and Result

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

• A variable (§4.12) (in C, this would be called an lvalue)

• A value (§4.2, §4.3)

• Nothing (the expression is said to be void)

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 float or double 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 void8.4). Such an expression can be used only as an expression statement (§14.8), because every other context in which an expression can appear requires the expression to denote something. An expression statement that is a method invocation may also invoke a method that produces a result; in this case the value returned by the method is quietly discarded.

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.2 Forms of Expressions

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

• Expression names (§6.5.6)

• Primary expressions (§15.8 - §15.13)

• Unary operator expressions (§15.14 - §15.16)

• Binary operator expressions (§15.17 - §15.24, and §15.26)

• Ternary operator expressions (§15.25)

• Lambda expressions (§15.27)

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:

Expression:
   LambdaExpression
  AssignmentExpression

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

• Parenthesized expressions (§15.8.5)

• Class instance creation expressions (§15.9)

• Method invocation expressions (§15.12)

• Method reference expressions (§15.13)

• Conditional expressions (§15.25)

• Lambda expressions (§15.27)

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

15.3 Type of an Expression

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 final is guaranteed to have a value that is either a null reference or an object whose class is F itself, because final types have no subclasses.

15.4 FP-strict Expressions

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

Every 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 strictfp modifier (§8.1.1.3, §8.4.3.5, §9.1.1.2), then the expression is FP-strict.

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

Note that an annotation’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 strictfp modifier.

Within an FP-strict expression, all intermediate values must be elements of the float value set or the double value set, implying that the results of all FPstrict 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.5 Expressions and Run-Time Checks

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

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

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

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

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

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

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

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

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

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

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

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

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

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

15.6 Normal and Abrupt Completion of Evaluation

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

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

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

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

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

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

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

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

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

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

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

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

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

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

A 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.7 Evaluation Order

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.

15.7.1 Evaluate Left-Hand Operand First

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.

Click here to view code image

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

This program produces the output:

9

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

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

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

Click here to view code image

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

This program produces the output:

12
12

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

See also the example in §15.26.2.

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

Click here to view code image

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:

Click here to view code image

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

That is, the left-hand operand forgetIt() of the operator / throws an exception before the right-hand operand is evaluated and its embedded assignment of 2 to j occurs.

15.7.2 Evaluate Operands before Operation

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

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

Example 15.7.2-1. Evaluation of Operands Before Operation

Click here to view code image

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:

Click here to view code image

java.lang.Exception: Shuffle off to Buffalo!

and not:

Click here to view code image

java.lang.ArithmeticException: / by zero

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

15.7.3 Evaluation Respects Parentheses and Precedence

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 nota-number (NaN) values.

For example, !(x<y) may not be rewritten as x>=y, because these expressions have different values if either x or y is NaN or both are NaN.

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

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

So, for example, the test program:

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

prints:

Infinity
1.6e+308

because the first expression overflows and the second does not.

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

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

15.7.4 Argument Lists are Evaluated Left-to-Right

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

Click here to view code image

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

This program produces the output:

going, going, gone

because the assignment of the string “gone” to s occurs after the first two arguments to print3 have been evaluated.

Example 15.7.4-2. Abrupt Completion of Argument Expression

Click here to view code image

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:

Click here to view code image

java.lang.Exception: oops, id=1

because the assignment of 3 to id is not executed.

15.7.5 Evaluation Order for Other Expressions

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:

• class instance creation expressions (§15.9.4)

• array creation expressions (§15.10.2)

• array access expressions (§15.10.4)

• method invocation expressions (§15.12.4)

• method reference expressions (§15.13.3)

• assignments involving array components (§15.26)

• lambda expressions (§15.27.4)

15.8 Primary Expressions

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.

Primary:
   PrimaryNoNewArray
   ArrayCreationExpression

PrimaryNoNewArray:
   Literal
   ClassLiteral
   this
   TypeName . this
   ( Expression )
   ClassInstanceCreationExpression
   FieldAccess
   ArrayAccess
   MethodInvocation
   MethodReference

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

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

The second unusual feature avoids a potential grammatical ambiguity in the expression “new int[3][3]” which in Java always means a single creation of a multidimensional array, but which, without appropriate grammatical finesse, might also be interpreted as meaning the same as “(new int[3])[3]”.

This ambiguity is eliminated by splitting the expected definition of Primary into Primary and PrimaryNoNewArray. (This may be compared to the splitting of Statement into Statement and StatementNoShortIf14.5) to avoid the “dangling else” problem.)

15.8.1 Lexical Literals

A literal (§3.10) denotes a fixed, unchanging value.

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

Literal:
    IntegerLiteral
    FloatingPointLiteral
    BooleanLiteral
    CharacterLiteral
    StringLiteral
    NullLiteral

The type of a literal is determined as follows:

• The type of an integer literal (§3.10.1) that ends with L or l is long4.2.1).

The type of any other integer literal is int4.2.1).

• The type of a floating-point literal (§3.10.2) that ends with F or f is float and its value must be an element of the float value set (§4.2.3).

The type of any other floating-point literal is double and its value must be an element of the double value set (§4.2.3).

• The type of a boolean literal (§3.10.3) is boolean4.2.5).

• The type of a character literal (§3.10.4) is char4.2.1).

• The type of a string literal (§3.10.5) is String4.3.3).

• The type of the null literal null3.10.7) is the null type (§4.1); its value is the null reference.

Evaluation of a lexical literal always completes normally.

15.8.2 Class Literals

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

ClassLiteral:
   TypeName {[ ]} . class
   NumericType {[ ]} . class
   boolean {[ ]} . class
   void . class

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

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

The type of void.class8.4.5) is Class<Void>.

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

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

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

15.8.3 this

The keyword this may be used only in the following contexts:

• in the body of an instance method or default method (§8.4.7, §9.4.3)

• in the body of a constructor of a class (§8.8.7)

• in an instance initializer of a class (§8.6)

• in the initializer of an instance variable of a class (§8.3.2)

• to denote a receiver parameter (§8.4.1)

If it appears anywhere else, a compile-time error occurs.

The keyword this may be used in a lambda expression only if it is allowed in the context in which the lambda expression appears. Otherwise, a compile-time error occurs.

When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method or default method was invoked (§15.12), or to the object being constructed. The value denoted by this in a lambda body is the same as the value denoted by this in the surrounding context.

The keyword this is also used in explicit constructor invocation statements (§8.8.7.1).

The type of this is the class or interface type T within which the keyword this occurs.

Default methods provide the unique ability to access this inside an interface. (All other interface methods are either abstract or static, so provide no access to this.) As a result, it is possible for this to have an interface type.

At run time, the class of the actual object referred to may be T, if T is a class type, or a class that is a subtype of T.

Example 15.8.3-1. The this Expression

Click here to view code image

class IntVector {
    int[] v;
    boolean equals(IntVector other) {
        if (this == other)
            return true;
        if (v.length != other.v.length)
            return false;
        for (int i = 0; i < v.length; i++) {
            if (v[i] != other.v[i]) return false;
        }
        return true;
    }
}

Here, the class IntVector implements a method equals, which compares two vectors. If the other vector is the same vector object as the one for which the equals method was invoked, then the check can skip the length and value comparisons. The equals method implements this check by comparing the reference to the other object to this.

15.8.4 Qualified this

Any lexically enclosing instance (§8.1.3) can be referred to by explicitly qualifying the keyword this.

Let T be the type denoted by TypeName. Let n be an integer such that T is the n’th lexically enclosing type declaration of the class or interface in which the qualified this expression appears.

The value of an expression of the form TypeName.this is the n’th lexically enclosing instance of this.

The type of the expression is T.

It is a compile-time error if the expression occurs in a class or interface which is not an inner class of class T or T itself.

15.8.5 Parenthesized Expressions

A parenthesized expression is a primary expression whose type is the type of the contained expression and whose value at run time is the value of the contained expression. If the contained expression denotes a variable then the parenthesized expression also denotes that variable.

The use of parentheses affects only the order of evaluation, except for a corner case whereby (-2147483648) and (-9223372036854775808L) are legal but -(2147483648) and -(9223372036854775808L) are illegal.

This is because the decimal literals 2147483648 and 9223372036854775808L are allowed only as an operand of the unary minus operator (§3.10.1).

In particular, the presence or absence of parentheses around an expression does not (except for the case noted above) affect in any way:

• the choice of value set (§4.2.3) for the value of an expression of type float or double.

• whether a variable is definitely assigned, definitely assigned when true, definitely assigned when false, definitely unassigned, definitely unassigned when true, or definitely unassigned when false16 (Definite Assignment)).

If a parenthesized expression appears in a context of a particular kind with target type T5 (Conversions and Contexts)), its contained expression similarly appears in a context of the same kind with target type T.

If the contained expression is a poly expression (§15.2), the parenthesized expression is also a poly expression. Otherwise, it is a standalone expression.

15.9 Class Instance Creation Expressions

A class instance creation expression is used to create new objects that are instances of classes.

ClassInstanceCreationExpression:
   UnqualifiedClassInstanceCreationExpression
   ExpressionName . UnqualifiedClassInstanceCreationExpression
   Primary . UnqualifiedClassInstanceCreationExpression

UnqualifiedClassInstanceCreationExpression:
   new [TypeArguments]
     ClassOrInterfaceTypeToInstantiate ( [ArgumentList] ) [ClassBody]

ClassOrInterfaceTypeToInstantiate:
   {Annotation} Identifier {. {Annotation} Identifier}
     [TypeArgumentsOrDiamond]

TypeArgumentsOrDiamond:
   TypeArguments
   <>

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

ArgumentList:
   Expression {, Expression}

A class instance creation expression specifies a class to be instantiated, possibly followed by type arguments (§4.5.1) or a diamond (<>) if the class being instantiated is generic (§8.1.2), followed by (a possibly empty) list of actual value arguments to the constructor.

If the type argument list to the class is empty — the diamond form <> — the type arguments of the class are inferred. It is legal, though strongly discouraged as a matter of style, to have white space between the “<” and “>” of a diamond.

If the constructor is generic (§8.8.4), the type arguments to the constructor may similarly either be inferred or passed explicitly. If passed explicitly, the type arguments to the constructor immediately follow the keyword new.

It is a compile-time error if a class instance creation expression provides type arguments to a constructor but uses the diamond form for type arguments to the class.

This rule is introduced because inference of a generic class’s type arguments may influence the constraints on a generic constructor’s type arguments.

If TypeArguments is present immediately after new, or immediately before (, then it is a compile-time error if any of the type arguments are wildcards (§4.5.1).

The exception types that a class instance creation expression can throw are specified in §11.2.1.

Class instance creation expressions have two forms:

Unqualified class instance creation expressions begin with the keyword new.

An unqualified class instance creation expression may be used to create an instance of a class, regardless of whether the class is a top level (§7.6), member (§8.5, §9.5), local (§14.3), or anonymous class (§15.9.5).

Qualified class instance creation expressions begin with a Primary expression or an ExpressionName.

A qualified class instance creation expression enables the creation of instances of inner member classes and their anonymous subclasses.

Both unqualified and qualified class instance creation expressions may optionally end with a class body. Such a class instance creation expression declares an anonymous class15.9.5) and creates an instance of it.

A class instance creation expression is a poly expression (§15.2) if it uses the diamond form for type arguments to the class, and it appears in an assignment context or an invocation context (§5.2, §5.3). Otherwise, it is a standalone expression.

We say that a class is instantiated when an instance of the class is created by a class instance creation expression. Class instantiation involves determining the class to be instantiated (§15.9.1), the enclosing instances (if any) of the newly created instance (§15.9.2), and the constructor to be invoked to create the new instance (§15.9.3).

15.9.1 Determining the Class being Instantiated

If the class instance creation expression ends in a class body, then the class being instantiated is an anonymous class. Then:

• If the class instance creation expression is unqualified:

The ClassOrInterfaceTypeToInstantiate must denote a class that is accessible, non-final, and not an enum type; or denote an interface that is accessible. Otherwise a compile-time error occurs.

If ClassOrInterfaceTypeToInstantiate ends with <>, then a compile-time error occurs.

If ClassOrInterfaceTypeToInstantiate ends with TypeArguments, then ClassOrInterfaceTypeToInstantiate must denote a well-formed parameterized type (§4.5), or a compile-time error occurs.

Let T be the type denoted by ClassOrInterfaceTypeToInstantiate. If T denotes a class, then an anonymous direct subclass of T is declared. If T denotes an interface, then an anonymous direct subclass of Object that implements T is declared. In either case, the body of the subclass is the ClassBody given in the class instance creation expression.

The class being instantiated is the anonymous subclass.

• If the class instance creation expression is qualified:

The ClassOrInterfaceTypeToInstantiate must unambiguously denote an inner class that is accessible, non-final, not an enum type, and a member of the compile-time type of the Primary expression or the ExpressionName. Otherwise, a compile-time error occurs.

If ClassOrInterfaceTypeToInstantiate ends with <>, then a compile-time error occurs.

If ClassOrInterfaceTypeToInstantiate ends with TypeArguments, then ClassOrInterfaceTypeToInstantiate must denote a well-formed parameterized type, or a compile-time error occurs.

Let T be the type denoted by ClassOrInterfaceTypeToInstantiate. An anonymous direct subclass of T is declared. The body of the subclass is the ClassBody given in the class instance creation expression.

The class being instantiated is the anonymous subclass.

If a class instance creation expression does not declare an anonymous class, then:

• If the class instance creation expression is unqualified:

The ClassOrInterfaceTypeToInstantiate must denote a class that is accessible, non-abstract, and not an enum type. Otherwise, a compile-time error occurs.

If ClassOrInterfaceTypeToInstantiate ends with <>, but the class denoted by ClassOrInterfaceTypeToInstantiate is not generic, then a compile-time error occurs.

If ClassOrInterfaceTypeToInstantiate ends with TypeArguments, then ClassOrInterfaceTypeToInstantiate must denote a well-formed parameterized class, or a compile-time error occurs.

The class being instantiated is the class denoted by ClassOrInterfaceTypeToInstantiate.

• If the class instance creation expression is qualified:

The ClassOrInterfaceTypeToInstantiate must unambiguously denote an inner class that is accessible, non-abstract, not an enum type, and a member of the compile-time type of the Primary expression or the ExpressionName.

If ClassOrInterfaceTypeToInstantiate ends with <>, and the class denoted by ClassOrInterfaceTypeToInstantiate is not generic, then a compile-time error occurs.

If ClassOrInterfaceTypeToInstantiate ends with TypeArguments, then ClassOrInterfaceTypeToInstantiate must denote a well-formed parameterized class, or a compile-time error occurs.

The class being instantiated is the class denoted by ClassOrInterfaceTypeToInstantiate.

15.9.2 Determining Enclosing Instances

Let C be the class being instantiated, and let i be the instance being created. If C is an inner class, then i may have an immediately enclosing instance8.1.3), determined as follows:

• If C is an anonymous class, then:

– If the class instance creation expression occurs in a static context, then i has no immediately enclosing instance.

– Otherwise, the immediately enclosing instance of i is this.

• If C is a local class, then:

– If C occurs in a static context, then i has no immediately enclosing instance.

– Otherwise, if the class instance creation expression occurs in a static context, then a compile-time error occurs.

– Otherwise, let O be the immediately enclosing class of C. Let n be an integer such that O is the n’th lexically enclosing type declaration of the class in which the class instance creation expression appears.

The immediately enclosing instance of i is the n’th lexically enclosing instance of this.

• If C is an inner member class, then:

– If the class instance creation expression is unqualified, then:

If the class instance creation expression occurs in a static context, then a compile-time error occurs.

Otherwise, if C is a member of a class enclosing the class in which the class instance creation expression appears, then let O be the immediately enclosing class of which C is a member. Let n be an integer such that O is the n’th lexically enclosing type declaration of the class in which the class instance creation expression appears.

The immediately enclosing instance of i is the n’th lexically enclosing instance of this.

Otherwise, a compile-time error occurs.

– If the class instance creation expression is qualified, then the immediately enclosing instance of i is the object that is the value of the Primary expression or the ExpressionName.

If C is an anonymous class, and its direct superclass S is an inner class, then i may have an immediately enclosing instance with respect to S, determined as follows:

• If S is a local class, then:

– If S occurs in a static context, then i has no immediately enclosing instance with respect to S.

– Otherwise, if the class instance creation expression occurs in a static context, then a compile-time error occurs.

– Otherwise, let O be the immediately enclosing class of S. Let n be an integer such that O is the n’th lexically enclosing type declaration of the class in which the class instance creation expression appears.

The immediately enclosing instance of i with respect to S is the n’th lexically enclosing instance of this.

• If S is an inner member class, then:

– If the class instance creation expression is unqualified, then:

If the class instance creation expression occurs in a static context, then a compile-time error occurs.

Otherwise, if S is a member of a class enclosing the class in which the class instance creation expression appears, then let O be the immediately enclosing class of which S is a member. Let n be an integer such that O is the n’th lexically enclosing type declaration of the class in which the class instance creation expression appears.

The immediately enclosing instance of i with respect to S is the n’th lexically enclosing instance of this.

Otherwise, a compile-time error occurs.

– If the class instance creation expression is qualified, then the immediately enclosing instance of i with respect to S is the object that is the value of the Primary expression or the ExpressionName.

15.9.3 Choosing the Constructor and its Arguments

Let C be the class being instantiated. To create an instance of C, i, a constructor of C is chosen at compile time by the following rules:

First, the actual arguments to the constructor invocation are determined:

• If C is an anonymous class with direct superclass S, then:

– If S is not an inner class, or if S is a local class that occurs in a static context, then the arguments to the constructor are the arguments in the argument list of the class instance creation expression, if any, in the order they appear in the expression.

– Otherwise, the first argument to the constructor is the immediately enclosing instance of i with respect to S15.9.2), and the subsequent arguments to the constructor are the arguments in the argument list of the class instance creation expression, if any, in the order they appear in the class instance creation expression.

• If C is a local class or a private inner member class, then the arguments to the constructor are the arguments in the argument list of the class instance creation expression, if any, in the order they appear in the class instance creation expression.

• If C is a non-private inner member class, then the first argument to the constructor is the immediately enclosing instance of i8.8.1, §15.9.2), and the subsequent arguments to its constructor are the arguments in the argument list of the class instance creation expression, if any, in the order they appear in the class instance creation expression.

• Otherwise, the arguments to the constructor are the arguments in the argument list of the class instance creation expression, if any, in the order they appear in the expression.

Second, a constructor of C and corresponding return type and throws clause are determined:

• If the class instance creation expression uses <> to elide class type arguments, a list of methods m1...mn is defined for the purpose of overload resolution and type argument inference.

Let c1...cn be the constructors of class C. Let #m be an automatically generated name that is distinct from all constructor and method names in C. For all j (1 ≤ jn), mj is defined in terms of cj as follows:

– A substitution θj is first defined to instantiate the types in cj.

Let F1...Fp be the type parameters of C, and let G1...Gq be the type parameters (if any) of cj. Let X1...Xp and Y1...Yq be type variables with distinct names that are not in scope in the body of C.

θj is [F1:=X1, ..., Fp:=Xp, G1:=Y1, ..., Gq:=Yq].

– The modifiers of mj are those of cj.

– The type parameters of mj are X1...Xp,Y1...Yq. The bound of each parameter, if any, is θj applied to the corresponding parameter bound in C or cj.

– The return type of mj is θj applied to C<F1,...,Fp>.

– The name of mj is #m.

– The (possibly empty) list of argument types of mj is θj applied to the argument types of cj.

– The (possibly empty) list of thrown types of mj is θj applied to the thrown types of cj.

– The body of mj is irrelevant.

To choose a constructor, we temporarily consider m1...mn to be members of C. Then one of m1...mn is selected, as determined by the class instance creation’s argument expressions, using the process specified in §15.12.2.

If there is no unique most specific method that is both applicable and accessible, then a compile-time error occurs.

Otherwise, where mj is the selected method, cj is the chosen constructor. The return type and throws clause of cj are the same as the return type and throws clause determined for mj15.12.2.6).

• Otherwise, the class instance creation expression does not use <> to elide class type arguments.

Let T be the type denoted by C followed by any class type arguments in the expression. The process specified in §15.12.2, modified to handle constructors, is used to select one of the constructors of T and determine its throws clause.

If there is no unique most-specific constructor that is both applicable and accessible, then a compile-time error occurs (as in method invocations).

Otherwise, the return type is T.

It is a compile-time error if an argument to a class instance creation expression is not compatible with its target type, as derived from the invocation type (§15.12.2.6).

If the compile-time declaration is applicable by variable arity invocation (§15.12.2.4), then where the last formal parameter type of the invocation type of the constructor is Fn[], it is a compile-time error if the type which is the erasure of Fn is not accessible at the point of invocation.

The type of the class instance creation expression is the return type of the chosen constructor, as defined above.

Note that the type of the class instance creation expression may be an anonymous class type, in which case the constructor being invoked is an anonymous constructor (§15.9.5.1).

15.9.4 Run-Time Evaluation of Class Instance Creation Expressions

At run time, evaluation of a class instance creation expression is as follows.

First, if the class instance creation expression is a qualified class instance creation expression, the qualifying primary expression is evaluated. If the qualifying expression evaluates to null, a NullPointerException is raised, and the class instance creation expression completes abruptly. If the qualifying expression completes abruptly, the class instance creation expression completes abruptly for the same reason.

Next, space is allocated for the new class instance. If there is insufficient space to allocate the object, evaluation of the class instance creation expression completes abruptly by throwing an OutOfMemoryError.

The new object contains new instances of all the fields declared in the specified class type and all its superclasses. As each new field instance is created, it is initialized to its default value (§4.12.5).

Next, the actual arguments to the constructor are evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.

Next, the selected constructor of the specified class type is invoked. This results in invoking at least one constructor for each superclass of the class type. This process can be directed by explicit constructor invocation statements (§8.8) and is specified in detail in §12.5.

The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created.

Example 15.9.4-1. Evaluation Order and Out-Of-Memory Detection

If evaluation of a class instance creation expression finds there is insufficient memory to perform the creation operation, then an OutOfMemoryError is thrown. This check occurs before any argument expressions are evaluated.

So, for example, the test program:

Click here to view code image

class List {
    int value;
    List next;
    static List head = new List(0);
    List(int n) { value = n; next = head; head = this; }
}
class Test {
    public static void main(String[] args) {
        int id = 0, oldid = 0;
        try {
            for (;;) {
                ++id;
                new List(oldid = id);
            }
        } catch (Error e) {
            List.head = null;
            System.out.println(e.getClass() + ", " + (oldid==id));
        }
    }
}

prints:

Click here to view code image

class java.lang.OutOfMemoryError, false

because the out-of-memory condition is detected before the argument expression oldid = id is evaluated.

Compare this to the treatment of array creation expressions, for which the out-of-memory condition is detected after evaluation of the dimension expressions (§15.10.2).

15.9.5 Anonymous Class Declarations

An anonymous class declaration is automatically derived from a class instance creation expression by the Java compiler.

An anonymous class is never abstract8.1.1.1).

An anonymous class is always implicitly final8.1.1.2).

An anonymous class is always an inner class (§8.1.3); it is never static8.1.1, §8.5.1).

15.9.5.1 Anonymous Constructors

An anonymous class cannot have an explicitly declared constructor. Instead, an anonymous constructor is implicitly declared for an anonymous class. The form of the anonymous constructor for an anonymous class C with direct superclass S is as follows:

• If S is not an inner class, or if S is a local class that occurs in a static context, then the anonymous constructor has one formal parameter for each actual argument to the class instance creation expression in which C is declared.

The actual arguments to the class instance creation expression are used to determine a constructor cs of S, using the same rules as for method invocations (§15.12). The type of each formal parameter of the anonymous constructor must be identical to the corresponding formal parameter of cs.

The constructor body consists of an explicit constructor invocation (§8.8.7.1) of the form super(...), where the actual arguments are the formal parameters of the constructor, in the order they were declared.

• Otherwise, the first formal parameter of the constructor of C represents the value of the immediately enclosing instance of i with respect to S15.9.2, §15.9.3). The type of this parameter is the class type that immediately encloses the declaration of S.

The constructor has an additional formal parameter for each actual argument to the class instance creation expression that declared the anonymous class. The n’th formal parameter e corresponds to the n-1’th actual argument.

The actual arguments to the class instance creation expression are used to determine a constructor cs of S, using the same rules as for method invocations (§15.12). The type of each formal parameter of the anonymous constructor must be identical to the corresponding formal parameter of cs.

The constructor body consists of an explicit constructor invocation (§8.8.7.1) of the form o.super(...), where o is the first formal parameter of the constructor, and the actual arguments are the subsequent formal parameters of the constructor, in the order they were declared.

In all cases, the throws clause of an anonymous constructor must list all the checked exceptions thrown by the explicit superclass constructor invocation statement contained within the anonymous constructor, and all checked exceptions thrown by any instance initializers or instance variable initializers of the anonymous class.

Note that it is possible for the signature of the anonymous constructor to refer to an inaccessible type (for example, if such a type occurred in the signature of the superclass constructor cs). This does not, in itself, cause any errors at either compile-time or run-time.

15.10 Array Creation and Access Expressions

15.10.1 Array Creation Expressions

An array creation expression is used to create new arrays (§10 (Arrays)).

ArrayCreationExpression:
   new PrimitiveType DimExprs [Dims]
   new ClassOrInterfaceType DimExprs [Dims]
   new PrimitiveType Dims ArrayInitializer
   new ClassOrInterfaceType Dims ArrayInitializer

DimExprs:
   DimExpr {DimExpr}

DimExpr:
   {Annotation} [ Expression ]

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

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

An array creation expression creates an object that is a new array whose elements are of the type specified by the PrimitiveType or ClassOrInterfaceType.

It is a compile-time error if the ClassOrInterfaceType does not denote a reifiable type (§4.7). Otherwise, the ClassOrInterfaceType may name any named reference type, even an abstract class type (§8.1.1.1) or an interface type.

The rules above imply that the element type in an array creation expression cannot be a parameterized type, unless all type arguments to the parameterized type are unbounded wildcards.

The type of each dimension expression within a DimExpr must be a type that is convertible (§5.1.8) to an integral type, or a compile-time error occurs.

Each dimension expression undergoes unary numeric promotion (§5.6.1). The promoted type must be int, or a compile-time error occurs.

The type of the array creation expression is an array type that can denoted by a copy of the array creation expression from which the new keyword and every DimExpr expression and array initializer have been deleted.

For example, the type of the creation expression:

new double[3][3][]

is:

double[][][]

15.10.2 Run-Time Evaluation of Array Creation Expressions

At run time, evaluation of an array creation expression behaves as follows:

• If there are no dimension expressions, then there must be an array initializer. A newly allocated array will be initialized with the values provided by the array initializer as described in §10.6. The value of the array initializer becomes the value of the array creation expression.

• Otherwise, there is no array initializer, and:

– First, the dimension expressions are evaluated, left-to-right. If any of the expression evaluations completes abruptly, the expressions to the right of it are not evaluated.

– Next, the values of the dimension expressions are checked. If the value of any DimExpr expression is less than zero, then a NegativeArraySizeException is thrown.

– Next, space is allocated for the new array. If there is insufficient space to allocate the array, evaluation of the array creation expression completes abruptly by throwing an OutOfMemoryError.

– Then, if a single DimExpr appears, a one-dimensional array is created of the specified length, and each component of the array is initialized to its default value (§4.12.5).

– Otherwise, if n DimExpr expressions appear, then array creation effectively executes a set of nested loops of depth n-1 to create the implied arrays of arrays.

A multidimensional array need not have arrays of the same length at each level.

Example 15.10.2-1. Array Creation Evaluation

In an array creation expression with one or more dimension expressions, each dimension expression is fully evaluated before any part of any dimension expression to its right. Thus:

Click here to view code image

class Test1 {
    public static void main(String[] args) {
        int i = 4;
        int ia[][] = new int[i][i=3];

        System.out.println(
            "[" + ia.length + "," + ia[0].length + "]");
    }
}

prints:

[4,3]

because the first dimension is calculated as 4 before the second dimension expression sets i to 3.

If evaluation of a dimension expression completes abruptly, no part of any dimension expression to its right will appear to have been evaluated. Thus:

Click here to view code image

class Test2 {
    public static void main(String[] args) {
        int[][] a = { { 00, 01 }, { 10, 11 } };
        int i = 99;
        try {
            a[val()][i = 1]++;
        } catch (Exception e) {
            System.out.println(e + ", i=" + i);
        }
    }
    static int val() throws Exception {
        throw new Exception("unimplemented");
    }
}

prints:

Click here to view code image

java.lang.Exception: unimplemented, i=99

because the embedded assignment that sets i to 1 is never executed.

Example 15.10.2-2. Multi-Dimensional Array Creation

The declaration:

Click here to view code image

float[][] matrix = new float[3][3];

is equivalent in behavior to:

Click here to view code image

float[][] matrix = new float[3][];
for (int d = 0; d < matrix.length; d++)
    matrix[d] = new float[3];

and:

Click here to view code image

Age[][][][][] Aquarius = new Age[6][10][8][12][];

is equivalent to:

Click here to view code image

Age[][][][][] Aquarius = new Age[6][][][][];
for (int d1 = 0; d1 < Aquarius.length; d1++) {
    Aquarius[d1] = new Age[10][][][];
    for (int d2 = 0; d2 < Aquarius[d1].length; d2++) {
        Aquarius[d1][d2] = new Age[8][][];
        for (int d3 = 0; d3 < Aquarius[d1][d2].length; d3++) {
            Aquarius[d1][d2][d3] = new Age[12][];
        }
    }
}

with d, d1, d2, and d3 replaced by names that are not already locally declared. Thus, a single new expression actually creates one array of length 6, 6 arrays of length 10, 6x10 = 60 arrays of length 8, and 6x10x8 = 480 arrays of length 12. This example leaves the fifth dimension, which would be arrays containing the actual array elements (references to Age objects), initialized only to null references. These arrays can be filled in later by other code, such as:

Click here to view code image

Age[] Hair = { new Age("quartz"), new Age("topaz") };
Aquarius[1][9][6][9] = Hair;

A triangular matrix may be created by:

Click here to view code image

float triang[][] = new float[100][];
for (int i = 0; i < triang.length; i++)
    triang[i] = new float[i+1];

If evaluation of an array creation expression finds there is insufficient memory to perform the creation operation, then an OutOfMemoryError is thrown. If the array creation expression does not have an array initializer, then this check occurs only after evaluation of all dimension expressions has completed normally. If the array creation expression does have an array initializer, then an OutOfMemoryError can occur when an object of reference type is allocated during evaluation of a variable initializer expression, or when space is allocated for an array to hold the values of a (possibly nested) array initializer.

Example 15.10.2-3. OutOfMemoryError and Dimension Expression Evaluation

class Test3 {
    public static void main(String[] args) {
        int len = 0, oldlen = 0;
        Object[] a = new Object[0];
        try {
            for (;;) {
                ++len;
                Object[] temp = new Object[oldlen = len];
                temp[0] = a;
                a = temp;
            }
        } catch (Error e) {
            System.out.println(e + ", " + (oldlen==len));
        }
    }
}

This program produces the output:

Click here to view code image

java.lang.OutOfMemoryError, true

because the out-of-memory condition is detected after the dimension expression oldlen = len is evaluated.

Compare this to class instance creation expressions (§15.9), which detect the out-of-memory condition before evaluating argument expressions (§15.9.4).

15.10.3 Array Access Expressions

An array access expression refers to a variable that is a component of an array.

ArrayAccess:
   ExpressionName [ Expression ]
   PrimaryNoNewArray [ Expression ]

An array access expression contains two subexpressions, the array reference expression (before the left bracket) and the index expression (within the brackets).

Note that the array reference expression may be a name or any primary expression that is not an array creation expression (§15.10).

The type of the array reference expression must be an array type (call it T[], an array whose components are of type T), or a compile-time error occurs.

The index expression undergoes unary numeric promotion (§5.6.1). The promoted type must be int, or a compile-time error occurs.

The type of the array access expression is the result of applying capture conversion (§5.1.10) to T.

The result of an array access expression is a variable of type T, namely the variable within the array selected by the value of the index expression.

This resulting variable, which is a component of the array, is never considered final, even if the array reference expression denoted a final variable.

15.10.4 Run-Time Evaluation of Array Access Expressions

At run time, evaluation of an array access expression behaves as follows:

• First, the array reference expression is evaluated. If this evaluation completes abruptly, then the array access completes abruptly for the same reason and the index expression is not evaluated.

• Otherwise, the index expression is evaluated. If this evaluation completes abruptly, then the array access completes abruptly for the same reason.

• Otherwise, if the value of the array reference expression is null, then a NullPointerException is thrown.

• Otherwise, the value of the array reference expression indeed refers to an array. If the value of the index expression is less than zero, or greater than or equal to the array’s length, then an ArrayIndexOutOfBoundsException is thrown.

• Otherwise, the result of the array access is the variable of type T, within the array, selected by the value of the index expression.

Example 15.10.4-1. Array Reference Is Evaluated First

In an array access, the expression to the left of the brackets appears to be fully evaluated before any part of the expression within the brackets is evaluated. For example, in the (admittedly monstrous) expression a[(a=b)[3]], the expression a is fully evaluated before the expression (a=b)[3]; this means that the original value of a is fetched and remembered while the expression (a=b)[3] is evaluated. This array referenced by the original value of a is then subscripted by a value that is element 3 of another array (possibly the same array) that was referenced by b and is now also referenced by a.

Thus, the program:

Click here to view code image

class Test1 {
    public static void main(String[] args) {
        int[] a = { 11, 12, 13, 14 };
        int[] b = { 0, 1, 2, 3 };
        System.out.println(a[(a=b)[3]]);
    }
}

prints:

14

because the monstrous expression’s value is equivalent to a[b[3]] or a[3] or 14.

Example 15.10.4-2. Abrupt Completion of Array Reference Evaluation

If evaluation of the expression to the left of the brackets completes abruptly, no part of the expression within the brackets will appear to have been evaluated. Thus, the program:

Click here to view code image

class Test2 {
    public static void main(String[] args) {
        int index = 1;
        try {
            skedaddle()[index=2]++;
        } catch (Exception e) {
            System.out.println(e + ", index=" + index);
        }
    }
    static int[] skedaddle() throws Exception {
        throw new Exception("Ciao");
    }
}

prints:

Click here to view code image

java.lang.Exception: Ciao, index=1

because the embedded assignment of 2 to index never occurs.

Example 15.10.4-3. null Array Reference

If the array reference expression produces null instead of a reference to an array, then a NullPointerException is thrown at run time, but only after all parts of the array access expression have been evaluated and only if these evaluations completed normally. Thus, the program:

Click here to view code image

class Test3 {
    public static void main(String[] args) {
        int index = 1;
        try {
            nada()[index=2]++;
        } catch (Exception e) {
            System.out.println(e + ", index=" + index);
        }
    }
    static int[] nada() { return null; }
}

prints:

Click here to view code image

java.lang.NullPointerException, index=2

because the embedded assignment of 2 to index occurs before the check for a null array reference expression. As a related example, the program:

Click here to view code image

class Test4 {
    public static void main(String[] args) {
        int[] a = null;
        try {
            int i = a[vamoose()];
            System.out.println(i);
        } catch (Exception e) {
            System.out.println(e);
        }
    }
    static int vamoose() throws Exception {
        throw new Exception("Twenty-three skidoo!");
    }
}

always prints:

Click here to view code image

java.lang.Exception: Twenty-three skidoo!

A NullPointerException never occurs, because the index expression must be completely evaluated before any further part of the array access occurs, and that includes the check as to whether the value of the array reference expression is null.

15.11 Field Access Expressions

A field access expression may access a field of an object or array, a reference to which is the value of either an expression or the special keyword super.

FieldAccess:
   Primary . Identifier
   super . Identifier
   TypeName . super . Identifier

The meaning of a field access expression is determined using the same rules as for qualified names (§6.5.6.2), but limited by the fact that an expression cannot denote a package, class type, or interface type.

It is also possible to refer to a field of the current instance or current class by using a simple name (§6.5.6.1).

15.11.1 Field Access Using a Primary

The type of the Primary must be a reference type T, or a compile-time error occurs.

The meaning of the field access expression is determined as follows:

• If the identifier names several accessible (§6.6) member fields in type T, then the field access is ambiguous and a compile-time error occurs.

• If the identifier does not name an accessible member field in type T, then the field access is undefined and a compile-time error occurs.

• Otherwise, the identifier names a single accessible member field in type T, and the type of the field access expression is the type of the member field after capture conversion (§5.1.10).

At run time, the result of the field access expression is computed as follows: (assuming that the program is correct with respect to definite assignment analysis, i.e. every blank final variable is definitely assigned before access)

• If the field is static:

– The Primary expression is evaluated, and the result is discarded. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.

– If the field is a non-blank final field, then the result is the value of the specified class variable in the class or interface that is the type of the Primary expression.

– If the field is not final, or is a blank final and the field access occurs in a static initializer or class variable initializer, then the result is a variable, namely, the specified class variable in the class that is the type of the Primary expression.

• If the field is not static:

– The Primary expression is evaluated. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.

– If the value of the Primary is null, then a NullPointerException is thrown.

– If the field is a non-blank final, then the result is the value of the named member field in type T found in the object referenced by the value of the Primary.

– If the field is not final, or is a blank final and the field access occurs in a constructor or instance variable initializer, then the result is a variable, namely the named member field in type T found in the object referenced by the value of the Primary.

Note that only the type of the Primary expression, not the class of the actual object referred to at run time, is used in determining which field to use.

Example 15.11.1-1. Static Binding for Field Access

Click here to view code image

class S           { int x = 0; }
class T extends S { int x = 1; }
class Test1 {
    public static void main(String[] args) {
        T t = new T();
        System.out.println("t.x=" + t.x + when("t", t));
        S s = new S();
        System.out.println("s.x=" + s.x + when("s", s));
        s = t;
        System.out.println("s.x=" + s.x + when("s", s));
    }
    static String when(String name, Object t) {
        return " when " + name + " holds a "
                        + t.getClass() + " at run time.";
    }
}

This program produces the output:

Click here to view code image

t.x=1 when t holds a class T at run time.
s.x=0 when s holds a class S at run time.
s.x=0 when s holds a class T at run time.

The last line shows that, indeed, the field that is accessed does not depend on the run-time class of the referenced object; even if s holds a reference to an object of class T, the expression s.x refers to the x field of class S, because the type of the expression s is S. Objects of class T contain two fields named x, one for class T and one for its superclass S.

This lack of dynamic lookup for field accesses allows programs to be run efficiently with straightforward implementations. The power of late binding and overriding is available, but only when instance methods are used. Consider the same example using instance methods to access the fields:

Click here to view code image

class S           { int x = 0; int z() { return x; } }
class T extends S { int x = 1; int z() { return x; } }
class Test2 {
     public static void main(String[] args) {
         T t = new T();
         System.out.println("t.z()=" + t.z() + when("t", t));
         S s = new S();
         System.out.println("s.z()=" + s.z() + when("s", s));
         s = t;
         System.out.println("s.z()=" + s.z() + when("s", s));
    }
    static String when(String name, Object t) {
         return " when " + name + " holds a "
                         + t.getClass() + " at run time.";
    }
}

Now the output is:

Click here to view code image

t.z()=1 when t holds a class T at run time.
s.z()=0 when s holds a class S at run time.
s.z()=1 when s holds a class T at run time.

The last line shows that, indeed, the method that is accessed does depend on the run-time class of the referenced object; when s holds a reference to an object of class T, the expression s.z() refers to the z method of class T, despite the fact that the type of the expression s is S. Method z of class T overrides method z of class S.

Example 15.11.1-2. Receiver Variable Is Irrelevant For static Field Access

The following program demonstrates that a null reference may be used to access a class (static) variable without causing an exception:

Click here to view code image

class Test3 {
    static String mountain = "Chocorua";
    static Test3 favorite(){
        System.out.print("Mount ");
        return null;
    }
    public static void main(String[] args) {
        System.out.println(favorite().mountain);
    }
}

It compiles, executes, and prints:

Mount Chocorua

Even though the result of favorite() is null, a NullPointerException is not thrown. That “Mount ” is printed demonstrates that the Primary expression is indeed fully evaluated at run time, despite the fact that only its type, not its value, is used to determine which field to access (because the field mountain is static).

15.11.2 Accessing Superclass Members using super

The form super.Identifier refers to the field named Identifier of the current object, but with the current object viewed as an instance of the superclass of the current class.

The form T.super.Identifier refers to the field named Identifier of the lexically enclosing instance corresponding to T, but with that instance viewed as an instance of the superclass of T.

The forms using the keyword super are valid only in an instance method, instance initializer, or constructor of a class, or in the initializer of an instance variable of a class. If they appear anywhere else, a compile-time error occurs.

These are exactly the same situations in which the keyword this may be used in a class declaration (§15.8.3).

It is a compile-time error if the forms using the keyword super appear in the declaration of class Object, since Object has no superclass.

Suppose that a field access expression super.f appears within class C, and the immediate superclass of C is class S. If f in S is accessible from class C6.6), then super.f is treated as if it had been the expression this.f in the body of class S. Otherwise, a compile-time error occurs.

Thus, super.f can access the field f that is accessible in class S, even if that field is hidden by a declaration of a field f in class C.

Suppose that a field access expression T.super.f appears within class C, and the immediate superclass of the class denoted by T is a class whose fully qualified name is S. If f in S is accessible from C, then T.super.f is treated as if it had been the expression this.f in the body of class S. Otherwise, a compile-time error occurs.

Thus, T.super.f can access the field f that is accessible in class S, even if that field is hidden by a declaration of a field f in class T.

It is a compile-time error if the current class is not an inner class of class T or T itself.

Example 15.11.2-1. The super Expression

Click here to view code image

interface I           { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1   { int x = 2; }
class T3 extends T2 {
    int x = 3;
    void test() {
        System.out.println("x= "          + x);
        System.out.println("super.x= "    + super.x);
        System.out.println("((T2)this).x= " + ((T2)this).x);
        System.out.println("((T1)this).x= " + ((T1)this).x);
        System.out.println("((I)this).x= "  + ((I)this).x);
    }
}
class Test {
    public static void main(String[] args) {
        new T3().test();
    }
}

This program produces the output:

x=               3
super.x=         2
((T2)this).x=    2
((T1)this).x=    1
((I)this).x=     0

Within class T3, the expression super.x has the same effect as ((T2)this).x when x has package access. Note that super.x is not specified in terms of a cast, due to difficulties around access to protected members of the superclass.

15.12 Method Invocation Expressions

A method invocation expression is used to invoke a class or instance method.

MethodInvocation:
   MethodName ( [ArgumentList] )
   TypeName . [TypeArguments] Identifier ( [ArgumentList] )
   ExpressionName . [TypeArguments] Identifier ( [ArgumentList] )
   Primary . [TypeArguments] Identifier ( [ArgumentList] )
   super . [TypeArguments] Identifier ( [ArgumentList] )
   TypeName . super . [TypeArguments] Identifier ( [ArgumentList] )

ArgumentList:
   Expression {, Expression}

Resolving a method name at compile time is more complicated than resolving a field name because of the possibility of method overloading. Invoking a method at run time is also more complicated than accessing a field because of the possibility of instance method overriding.

Determining the method that will be invoked by a method invocation expression involves several steps. The following three sections describe the compile-time processing of a method invocation. The determination of the type of the method invocation expression is specified in §15.12.3.

The exception types that a method invocation expression can throw are specified in §11.2.1.

It is a compile-time error if the name to the left of the rightmost “.” that occurs before the ( in a MethodInvocation cannot be classified as a TypeName or an ExpressionName6.5.2).

If TypeArguments is present to the left of Identifier, then it is a compile-time error if any of the type arguments are wildcards (§4.5.1).

A method invocation expression is a poly expression if all of the following are true:

• The invocation appears in an assignment context or an invocation context (§5.2, §5.3).

• If the invocation is qualified (that is, any form of MethodInvocation except for the first), then the invocation elides TypeArguments to the left of the Identifier.

• The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a return type that mentions at least one of the method’s type parameters.

Otherwise, the method invocation expression is a standalone expression.

15.12.1 Compile-Time Step 1: Determine Class or Interface to Search

The first step in processing a method invocation at compile time is to figure out the name of the method to be invoked and which class or interface to search for definitions of methods of that name.

The name of the method is specified by the MethodName or Identifier which immediately precedes the left parenthesis of the MethodInvocation.

For the class or interface to search, there are six cases to consider, depending on the form that precedes the left parenthesis of the MethodInvocation:

• If the form is MethodName, that is, just an Identifier, then:

If the Identifier appears in the scope of a visible method declaration with that name (§6.3, §6.4.1), then:

– If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or interface to search is T.

This search policy is called the “comb rule”. It effectively looks for methods in a nested class’s superclass hierarchy before looking for methods in an enclosing class and its superclass hierarchy. See §6.5.7.1 for an example.

– Otherwise, the visible method declaration may be in scope due to one or more single-static-import or static-import-on-demand declarations. There is no class or interface to search, as the method to be invoked is determined later (§15.12.2.1).

• If the form is TypeName . [TypeArguments] Identifier, then the type to search is the type denoted by TypeName.

• If the form is ExpressionName . [TypeArguments] Identifier, then the class or interface to search is the declared type T of the variable denoted by ExpressionName if T is a class or interface type, or the upper bound of T if T is a type variable.

• If the form is Primary . [TypeArguments] Identifier, then let T be the type of the Primary expression. The class or interface to search is T if T is a class or interface type, or the upper bound of T if T is a type variable.

It is a compile-time error if T is not a reference type.

• If the form is super . [TypeArguments] Identifier, then the class to search is the superclass of the class whose declaration contains the method invocation.

Let T be the type declaration immediately enclosing the method invocation. It is a compile-time error if T is the class Object or T is an interface.

• If the form is TypeName . super . [TypeArguments] Identifier, then:

– It is a compile-time error if TypeName denotes neither a class nor an interface.

– If TypeName denote a class, C, then the class to search is the superclass of C.

It is a compile-time error if C is not a lexically enclosing type declaration of the current class, or if C is the class Object.

Let T be the type declaration immediately enclosing the method invocation. It is a compile-time error if T is the class Object.

– Otherwise, TypeName denotes the interface to be searched, I.

Let T be the type declaration immediately enclosing the method invocation. 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.

The TypeName . super syntax is overloaded: traditionally, the TypeName refers to a lexically enclosing type declaration which is a class, and the target is the superclass of this class, as if the invocation were an unqualified super in the lexically enclosing type declaration.

Click here to view code image

class Superclass {
    void foo() { System.out.println("Hi"); }
}

class Subclass1 extends Superclass {
    void foo() { throw new UnsupportedOperationException(); }

    Runnable tweak = new Runnable() {
        void run() {
            Subclass1.super.foo();  // Gets the 'println' behavior
        }
    };
}

To support invocation of default methods in superinterfaces, the TypeName may also refer to a direct superinterface of the current class or interface, and the target is that superinterface.

Click here to view code image

interface Superinterface {
    default void foo() { System.out.println("Hi"); }
}

class Subclass2 implements Superinterface {
    void foo() { throw new UnsupportedOperationException(); }

    void tweak() {
        Superinterface.super.foo();  // Gets the 'println' behavior
    }
}

No syntax supports a combination of these forms, that is, invoking a superinterface method of a lexically enclosing type declaration which is a class, as if the invocation were of the form InterfaceName . super in the lexically enclosing type declaration.

Click here to view code image

class Subclass3 implements Superinterface {
    void foo() { throw new UnsupportedOperationException(); }

    Runnable tweak = new Runnable() {
        void run() {
            Subclass3.Superinterface.super.foo();  // Illegal
        }
    };
}

A workaround is to introduce a private method in the lexically enclosing type declaration, that performs the interface super call.

15.12.2 Compile-Time Step 2: Determine Method Signature

The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.

There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is the one used at run time to perform the method dispatch.

A method is applicable if it is applicable by one of strict invocation (§15.12.2.2), loose invocation (§15.12.2.3), or variable arity invocation (§15.12.2.4).

Certain argument expressions that contain implicitly typed lambda expressions (§15.27.1) or inexact method references (§15.13.1) are ignored by the applicability tests, because their meaning cannot be determined until a target type is selected.

Although the method invocation may be a poly expression, only its argument expressions - not the invocation’s target type - influence the selection of applicable methods.

The process of determining applicability begins by determining the potentially applicable methods (§15.12.2.1).

The remainder of the process is split into three phases, to ensure compatibility with versions of the Java programming language prior to Java SE 5.0. The phases are:

1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous as the result of the introduction of variable arity methods, implicit boxing and/or unboxing. However, the declaration of a variable arity method (§8.4.1) can change the method chosen for a given method method invocation expression, because a variable arity method is treated as a fixed arity method in the first phase. For example, declaring m(Object...) in a class which already declares m(Object) causes m(Object) to no longer be chosen for some invocation expressions (such as m(null)), as m(Object[]) is more specific.

2. The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.

This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method invocation.

3. The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.

Deciding whether a method is applicable will, in the case of generic methods (§8.4.4), require an analysis of the type arguments. Type arguments may be passed explicitly or implicitly. If they are passed implicitly, bounds of the type arguments must be inferred (§18 (Type Inference)) from the argument expressions.

If several applicable methods have been identified during one of the three phases of applicability testing, then the most specific one is chosen, as specified in section §15.12.2.5.

To check for applicability, the types of an invocation’s arguments cannot, in general, be inputs to the analysis. This is because:

• The arguments to a method invocation may be poly expressions

• Poly expressions cannot be typed in the absence of a target type

• Overload resolution has to be completed before the arguments’ target types will be known

Instead, the input to the applicability check is a list of argument expressions, which can be checked for compatibility with potential target types, even if the ultimate types of the expressions are unknown.

Note that overload resolution is independent of a target type. This is for two reasons:

• First, it makes the user model more accessible and less error-prone. The meaning of a method name (i.e., the declaration corresponding to the name) is too fundamental to the meaning of a program to depend on subtle contextual hints. (In contrast, other poly expressions may have different behavior depending on a target type; but the variation in behavior is always limited and essentially equivalent, while no such guarantees can be made about the behavior of an arbitrary set of methods that share a name and arity.)

• Second, it allows other properties - such as whether or not the method is a poly expression (§15.12) or how to categorize a conditional expression (§15.25) - to depend on the meaning of the method name, even before a target type is known.

Example 15.12.2-1. Method Applicability

Click here to view code image

class Doubler {
            static int two()       { return two(1); }
    private static int two(int i)  { return 2*i;    }
}
class Test extends Doubler {  
    static long two(long j) { return j+j; }

    public static void main(String[] args) {
        System.out.println(two(3));
        System.out.println(Doubler.two(3)); // compile-time error
    }
}

For the method invocation two(1) within class Doubler, there are two accessible methods named two, but only the second one is applicable, and so that is the one invoked at run time.

For the method invocation two(3) within class Test, there are two applicable methods, but only the one in class Test is accessible, and so that is the one to be invoked at run time (the argument 3 is converted to type long).

For the method invocation Doubler.two(3), the class Doubler, not class Test, is searched for methods named two; the only applicable method is not accessible, and so this method invocation causes a compile-time error.

Another example is:

Click here to view code image

class ColoredPoint {
    int x, y;
    byte color;
    void setColor(byte color) { this.color = color; }
}
class Test {
    public static void main(String[] args) {
        ColoredPoint cp = new ColoredPoint();
        byte color = 37;
        cp.setColor(color);
        cp.setColor(37);  // compile-time error
    }
}

Here, a compile-time error occurs for the second invocation of setColor, because no applicable method can be found at compile time. The type of the literal 37 is int, and int cannot be converted to byte by invocation conversion. Assignment conversion, which is used in the initialization of the variable color, performs an implicit conversion of the constant from type int to byte, which is permitted because the value 37 is small enough to be represented in type byte; but such a conversion is not allowed for invocation conversion.

If the method setColor had, however, been declared to take an int instead of a byte, then both method invocations would be correct; the first invocation would be allowed because invocation conversion does permit a widening conversion from byte to int. However, a narrowing cast would then be required in the body of setColor:

Click here to view code image

void setColor(int color) { this.color = (byte)color; }

Here is an example of overloading ambiguity. Consider the program:

Click here to view code image

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    static void test(ColoredPoint p, Point q) {
        System.out.println("(ColoredPoint, Point)");
    }
    static void test(Point p, ColoredPoint q) {
        System.out.println("(Point, ColoredPoint)");
    }
    public static void main(String[] args) {
        ColoredPoint cp = new ColoredPoint();
        test(cp, cp);  // compile-time error
    }
}

This example produces an error at compile time. The problem is that there are two declarations of test that are applicable and accessible, and neither is more specific than the other. Therefore, the method invocation is ambiguous.

If a third definition of test were added:

Click here to view code image

static void test(ColoredPoint p, ColoredPoint q) {
    System.out.println("(ColoredPoint, ColoredPoint)");
}

then it would be more specific than the other two, and the method invocation would no longer be ambiguous.

Example 15.12.2-2. Return Type Not Considered During Method Selection

Click here to view code image

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    static int test(ColoredPoint p) {
        return p.color;
    }
    static String test(Point p) {
        return "Point";
    }
    public static void main(String[] args) {
        ColoredPoint cp = new ColoredPoint();
        String s = test(cp);  // compile-time error
    }
}

Here, the most specific declaration of method test is the one taking a parameter of type ColoredPoint. Because the result type of the method is int, a compile-time error occurs because an int cannot be converted to a String by assignment conversion. This example shows that the result types of methods do not participate in resolving overloaded methods, so that the second test method, which returns a String, is not chosen, even though it has a result type that would allow the example program to compile without error.

Example 15.12.2-3. Choosing The Most Specific Method

The most specific method is chosen at compile time; its descriptor determines what method is actually executed at run time. If a new method is added to a class, then source code that was compiled with the old definition of the class might not use the new method, even if a recompilation would cause this method to be chosen.

So, for example, consider two compilation units, one for class Point:

Click here to view code image

package points;
public class Point {
    public int x, y;
    public Point(int x, int y) { this.x = x; this.y = y; }
    public String toString() { return toString(""); }
    public String toString(String s) {
        return "(" + x + "," + y + s + ")";
    }
}

and one for class ColoredPoint:

Click here to view code image

package points;
public class ColoredPoint extends Point {
    public static final int
        RED = 0, GREEN = 1, BLUE = 2;
    public static String[] COLORS =
        { "red", "green", "blue" };

    public byte color;
    public ColoredPoint(int x, int y, int color) {
        super(x, y);
        this.color = (byte)color;
    }

    /** Copy all relevant fields of the argument into
        this ColoredPoint object. */
    public void adopt(Point p) { x = p.x; y = p.y; }

    public String toString() {
        String s = "," + COLORS[color];
        return super.toString(s);
    }
}

Now consider a third compilation unit that uses ColoredPoint:

Click here to view code image

import points.*;
class Test {
    public static void main(String[] args) {
        ColoredPoint cp =
            new ColoredPoint(6, 6, ColoredPoint.RED);
        ColoredPoint cp2 =
            new ColoredPoint(3, 3, ColoredPoint.GREEN);
        cp.adopt(cp2);
        System.out.println("cp: " + cp);
    }
}

The output is:

cp: (3,3,red)

The programmer who coded class Test has expected to see the word green, because the actual argument, a ColoredPoint, has a color field, and color would seem to be a “relevant field”. (Of course, the documentation for the package points ought to have been much more precise!)

Notice, by the way, that the most specific method (indeed, the only applicable method) for the method invocation of adopt has a signature that indicates a method of one parameter, and the parameter is of type Point. This signature becomes part of the binary representation of class Test produced by the Java compiler and is used by the method invocation at run time.

Suppose the programmer reported this software error and the maintainer of the points package decided, after due deliberation, to correct it by adding a method to class ColoredPoint:

Click here to view code image

public void adopt(ColoredPoint p) {
    adopt((Point)p);
    color = p.color;
}

If the programmer then runs the old binary file for Test with the new binary file for ColoredPoint, the output is still:

cp: (3,3,red)

because the old binary file for Test still has the descriptor “one parameter, whose type is Point; void” associated with the method call cp.adopt(cp2). If the source code for Test is recompiled, the Java compiler will then discover that there are now two applicable adopt methods, and that the signature for the more specific one is “one parameter, whose type is ColoredPoint; void”; running the program will then produce the desired output:

cp: (3,3,green)

With forethought about such problems, the maintainer of the points package could fix the ColoredPoint class to work with both newly compiled and old code, by adding defensive code to the old adopt method for the sake of old code that still invokes it on ColoredPoint arguments:

Click here to view code image

public void adopt(Point p) {
    if (p instanceof ColoredPoint)
        color = ((ColoredPoint)p).color;
    x = p.x; y = p.y;
}

Ideally, source code should be recompiled whenever code that it depends on is changed. However, in an environment where different classes are maintained by different organizations, this is not always feasible. Defensive programming with careful attention to the problems of class evolution can make upgraded code much more robust. See §13 (Binary Compatibility) for a detailed discussion of binary compatibility and type evolution.

15.12.2.1 Identify Potentially Applicable Methods

The class or interface determined by compile-time step 1 (§15.12.1) is searched for all member methods that are potentially applicable to this method invocation; members inherited from superclasses and superinterfaces are included in this search.

In addition, if the form of the method invocation expression is MethodName - that is, a single Identifier - then the search for potentially applicable methods also examines all member methods that are imported by single-static-import declarations and static-import-on-demand declarations of the compilation unit where the method invocation occurs (§7.5.3, §7.5.4) and that are not shadowed at the point where the method invocation appears.

A member method is potentially applicable to a method invocation if and only if all of the following are true:

• The name of the member is identical to the name of the method in the method invocation.

• The member is accessible (§6.6) to the class or interface in which the method invocation appears.

Whether a member method is accessible at a method invocation depends on the access modifier (public, protected, no modifier (package access), or private) in the member’s declaration and on where the method invocation appears.

• If the member is a fixed arity method with arity n, the arity of the method invocation is equal to n, and for all i (1 ≤ in), the i’th argument of the method invocation is potentially compatible, as defined below, with the type of the i’th parameter of the method.

• If the member is a variable arity method with arity n, then for all i (1 ≤ in-1), the i’th argument of the method invocation is potentially compatible with the type of the i’th parameter of the method; and, where the nth parameter of the method has type T[], one of the following is true:

– The arity of the method invocation is equal to n-1.

– The arity of the method invocation is equal to n, and the nth argument of the method invocation is potentially compatible with either T or T[].

– The arity of the method invocation is m, where m > n, and for all i (nim), the i’th argument of the method invocation is potentially compatible with T.

• If the method invocation includes explicit type arguments, and the member is a generic method, then the number of type arguments is equal to the number of type parameters of the method.

This clause implies that a non-generic method may be potentially applicable to an invocation that supplies explicit type arguments. Indeed, it may turn out to be applicable. In such a case, the type arguments will simply be ignored.

This rule stems from issues of compatibility and principles of substitutability. Since interfaces or superclasses may be generified independently of their subtypes, we may override a generic method with a non-generic one. However, the overriding (non-generic) method must be applicable to calls to the generic method, including calls that explicitly pass type arguments. Otherwise the subtype would not be substitutable for its generified supertype.

If the search does not yield at least one method that is potentially applicable, then a compile-time error occurs.

An expression is potentially compatible with a target type according to the following rules:

• A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:

– The arity of the target type’s function type is the same as the arity of the lambda expression.

– If the target type’s function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).

– If the target type’s function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).

• A method reference expression (§15.13) is potentially compatible with a functional interface type if, where the type’s function type arity is n, there exists at least one potentially applicable method for the method reference expression with arity n15.13.1), and one of the following is true:

– The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is i) static and supports arity n, or ii) not static and supports arity n-1.

– The method reference expression has some other form and at least one potentially applicable method is not static.

• A lambda expression or a method reference expression is potentially compatible with a type variable if the type variable is a type parameter of the candidate method.

• A parenthesized expression (§15.8.5) is potentially compatible with a type if its contained expression is potentially compatible with that type.

• A conditional expression (§15.25) is potentially compatible with a type if each of its second and third operand expressions are potentially compatible with that type.

• A class instance creation expression, a method invocation expression, or an expression of a standalone form (§15.2) is potentially compatible with any type.

The definition of potential applicability goes beyond a basic arity check to also take into account the presence and “shape” of functional interface target types. In some cases involving type argument inference, a lambda expression appearing as a method invocation argument cannot be properly typed until after overload resolution. These rules allow the form of the lambda expression to still be taken into account, discarding obviously incorrect target types that might otherwise cause ambiguity errors.

15.12.2.2 Phase 1: Identify Matching Arity Methods Applicable by Strict Invocation

An argument expression is considered pertinent to applicability for a potentially applicable method m unless it has one of the following forms:

• An implicitly typed lambda expression (§15.27.1).

• An inexact method reference expression (§15.13.1).

• If m is a generic method and the method invocation does not provide explicit type arguments, an explicitly typed lambda expression or an exact method reference expression for which the corresponding target type (as derived from the signature of m) is a type parameter of m.

• An explicitly typed lambda expression whose body is an expression that is not pertinent to applicability.

• An explicitly typed lambda expression whose body is a block, where at least one result expression is not pertinent to applicability.

• A parenthesized expression (§15.8.5) whose contained expression is not pertinent to applicability.

• A conditional expression (§15.25) whose second or third operand is not pertinent to applicability.

Let m be a potentially applicable method (§15.12.2.1) with arity n and formal parameter types F1 ... Fn, and let e1, ..., en be the actual argument expressions of the method invocation. Then:

• If m is a generic method and the method invocation does not provide explicit type arguments, then the applicability of the method is inferred as specified in §18.5.1.

• If m is a generic method and the method invocation provides explicit type arguments, then let R1 ... Rp (p ≥ 1) be the type parameters of m, let Bl be the declared bound of Rl (1 ≤ lp), and let U1, ..., Up be the explicit type arguments given in the method invocation. Then m is applicable by strict invocation if both of the following are true:

– For 1 ≤ in, if ei is pertinent to applicability then ei is compatible in a strict invocation context with Fi[R1:=U1, ..., Rp:=Up].

– For 1 ≤ lp, Ul <: Bl[R1:=U1, ..., Rp:=Up].

• If m is not a generic method, then m is applicable by strict invocation if, for 1 ≤ in, either ei is compatible in a strict invocation context with Fi or ei is not pertinent to applicability.

If no method applicable by strict invocation is found, the search for applicable methods continues with phase 2 (§15.12.2.3).

Otherwise, the most specific method (§15.12.2.5) is chosen among the methods that are applicable by strict invocation.

The meaning of an implicitly typed lambda expression or an inexact method reference expression is sufficiently vague prior to resolving a target type that arguments containing these expressions are not considered pertinent to applicability; they are simply ignored (except for their expected arity) until overload resolution is finished.

15.12.2.3 Phase 2: Identify Matching Arity Methods Applicable by Loose Invocation

Let m be a potentially applicable method (§15.12.2.1) with arity n and formal parameter types F1, ..., Fn, and let e1, ..., en be the actual argument expressions of the method invocation. Then:

• If m is a generic method and the method invocation does not provide explicit type arguments, then the applicability of the method is inferred as specified in §18.5.1.

• If m is a generic method and the method invocation provides explicit type arguments, then let R1 ... Rp (p ≥ 1) be the type parameters of m, let Bl be the declared bound of Rl (1 ≤ lp), and let U1 ... Up be the explicit type arguments given in the method invocation. Then m is applicable by loose invocation if both of the following are true:

– For 1 ≤ in, if ei is pertinent to applicability (§15.12.2.2) then ei is compatible in a loose invocation context with Fi[R1:=U1, ..., Rp:=Up].

– For 1 ≤ lp, Ul <: Bl[R1:=U1, ..., Rp:=Up].

• If m is not a generic method, then m is applicable by loose invocation if, for 1 ≤ in, either ei is compatible in a loose invocation context with Fi or ei is not pertinent to applicability.

If no method applicable by loose invocation is found, the search for applicable methods continues with phase 3 (§15.12.2.4).

Otherwise, the most specific method (§15.12.2.5) is chosen among the methods that are applicable by loose invocation.

15.12.2.4 Phase 3: Identify Methods Applicable by Variable Arity Invocation

Where a variable arity method has formal parameter types F1, ..., Fn-1, Fn[], let the i’th variable arity parameter type of the method be defined as follows:

• For in-1, the i’th variable arity parameter type is Fi.

• For in, the i’th variable arity parameter type is Fn.

Let m be a potentially applicable method (§15.12.2.1) with variable arity, let T1, ..., Tk be the first k variable arity parameter types of m, and let e1, ..., ek be the actual argument expressions of the method invocation. Then:

• If m is a generic method and the method invocation does not provide explicit type arguments, then the applicability of the method is inferred as specified in §18.5.1.

• If m is a generic method and the method invocation provides explicit type arguments, then let R1 ... Rp (p ≥ 1) be the type parameters of m, let Bl be the declared bound of Rl (1 ≤ lp), and let U1 ... Up be the explicit type arguments given in the method invocation. Then m is applicable by variable arity invocation if:

– For 1 ≤ ik, if ei is pertinent to applicability (§15.12.2.2) then ei is compatible in a loose invocation context with Ti[R1:=U1, ..., Rp:=Up].

– For 1 ≤ lp, Ul <: Bl[R1:=U1, ..., Rp:=Up].

• If m is not a generic method, then m is applicable by variable arity invocation if, for 1 ≤ ik, either ei is compatible in a loose invocation context with Ti or ei is not pertinent to applicability (§15.12.2.2).

If no method applicable by variable arity invocation is found, a compile-time error occurs.

Otherwise, the most specific method (§15.12.2.5) is chosen among the methods applicable by variable arity invocation.

15.12.2.5 Choosing the Most Specific Method

If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the runtime method dispatch. The Java programming language uses the rule that the most specific method is chosen.

The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error. In cases such as an explicitly typed lambda expression argument (§15.27.1) or a variable arity invocation (§15.12.2.4), some flexibility is allowed to adapt one signature to the other.

One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:

m2 is generic and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.

m2 is not generic, m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ in, n = k).

m2 is not generic, m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ ik). Additionally, if m2 has k+1 parameters, then the k+1’th variable arity parameter type of m1 is a subtype of the k+1’th variable arity parameter type of m2.

The above conditions are the only circumstances under which one method may be more specific than another.

A type S is more specific than a type T for any expression if S <: T4.10).

A functional interface type S is more specific than a functional interface type T for an expression e if T is not a subtype of S and one of the following is true (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of S, and V1 ... Vk and R2 are the parameter types and return type of the function type of T):

• If e is an explicitly typed lambda expression (§15.27.1), then one of the following is true:

R2 is void.

R1 <: R2.

R1 and R2 are functional interface types, and R1 is more specific than R2 for each result expression of e.

The result expression of a lambda expression with a block body is defined in §15.27.2; the result expression of a lambda expression with an expression body is simply the body itself.

R1 is a primitive type, R2 is a reference type, and each result expression of e is a standalone expression (§15.2) of a primitive type.

R1 is a reference type, R2 is a primitive type, and each result expression of e is either a standalone expression of a reference type or a poly expression.

• If e is an exact method reference expression (§15.13.1), then i) for all i (1 ≤ ik), Ui is the same as Vi, and ii) one of the following is true:

R2 is void.

R1 <: R2.

R1 is a primitive type, R2 is a reference type, and the compile-time declaration for the method reference has a return type which is a primitive type.

R1 is a reference type, R2 is a primitive type, and the compile-time declaration for the method reference has a return type which is a reference type.

• If e is a parenthesized expression, then one of these conditions applies recursively to the contained expression.

• If e is a conditional expression, then for each of the second and third operands, one of these conditions applies recursively.

A method m1 is strictly more specific than another method m2 if and only if m1 is more specific than m2 and m2 is not more specific than m1.

A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific.

If there is exactly one maximally specific method, then that method is in fact the most specific method; it is necessarily more specific than any other accessible method that is applicable. It is then subjected to some further compile-time checks as specified in §15.12.3.

It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:

• If all the maximally specific methods have override-equivalent signatures (§8.4.2), then:

– If exactly one of the maximally specific methods is concrete (that is, non abstract or default), it is the most specific method.

– Otherwise, if all the maximally specific methods are abstract or default, and the signatures of all of the maximally specific methods have the same erasure (§4.6), then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type.

In this case, the most specific method is considered to be abstract. Also, the most specific method is considered to throw a checked exception if and only if that exception or its erasure is declared in the throws clauses of each of the maximally specific methods.

• Otherwise, the method invocation is ambiguous, and a compile-time error occurs.

15.12.2.6 Method Invocation Type

The invocation type of a most specific accessible and applicable method is a method type (§8.2) expressing the target types of the invocation arguments, the result (return type or void) of the invocation, and the exception types of the invocation. It is determined as follows:

• If the chosen method is generic and the method invocation does not provide explicit type arguments, the invocation type is inferred as specified in §18.5.2.

• If the chosen method is generic and the method invocation provides explicit type arguments, let Pi be the type parameters of the method and let Ti be the explicit type arguments provided for the method invocation (1 ≤ ip). Then:

– If unchecked conversion was necessary for the method to be applicable, then the invocation type’s parameter types are obtained by applying the substitution [P1:=T1, ..., Pp:=Tp] to the parameter types of the method’s type, and the invocation type’s return type and thrown types are given by the erasure of the return type and thrown types of the method’s type.

– If unchecked conversion was not necessary for the method to be applicable, then the invocation type is obtained by applying the substitution [P1:=T1, ..., Pp:=Tp] to the method’s type.

• If the chosen method is not generic, then:

– If unchecked conversion was necessary for the method to be applicable, the parameter types of the invocation type are the parameter types of the method’s type, and the return type and thrown types are given by the erasures of the return type and thrown types of the method’s type.

– Otherwise, if the chosen method is the getClass method of the class Object4.3.2), the invocation type is the same as the method’s type, except that the return type is Class<? extends T>, where T is the type that was searched, as determined by §15.12.1.

– Otherwise, the invocation type is the same as the method’s type.

15.12.3 Compile-Time Step 3: Is the Chosen Method Appropriate?

If there is a most specific method declaration for a method invocation, it is called the compile-time declaration for the method invocation.

It is a compile-time error if an argument to a method invocation is not compatible with its target type, as derived from the invocation type of the compile-time declaration.

If the compile-time declaration is applicable by variable arity invocation, then where the last formal parameter type of the invocation type of the method is Fn[], it is a compile-time error if the type which is the erasure of Fn is not accessible at the point of invocation.

If the compile-time declaration is void, then the method invocation must be a top level expression (that is, the Expression in an expression statement or in the ForInit or ForUpdate part of a for statement), or a compile-time error occurs. Such a method invocation produces no value and so must be used only in a situation where a value is not needed.

In addition, whether the compile-time declaration is appropriate may depend on the form of the method invocation expression before the left parenthesis, as follows:

• If the form is MethodName - that is, just an Identifier - and the compile-time declaration is an instance method, then:

– It is a compile-time error if the method invocation occurs in a static context (§8.1.3).

– Otherwise, let C be the immediately enclosing class of which the compile-time declaration is a member. If the method invocation is not directly enclosed by C or an inner class of C, then a compile-time error occurs.

• If the form is TypeName . [TypeArguments] Identifier, then the compile-time declaration must be static, or a compile-time error occurs.

• If the form is ExpressionName . [TypeArguments] Identifier or Primary . [TypeArguments] Identifier, then the compile-time declaration must not be a static method declared in an interface, or a compile-time error occurs.

• If the form is super . [TypeArguments] Identifier, then:

– It is a compile-time error if the compile-time declaration is abstract.

– It is a compile-time error if the method invocation occurs in a static context.

• If the form is TypeName . super . [TypeArguments] Identifier, then:

– It is a compile-time error if the compile-time declaration is abstract.

– It is a compile-time error if the method invocation occurs in a static context.

– If TypeName denotes a class C, then if the method invocation is not directly enclosed by C or an inner class of C, a compile-time error occurs.

– If TypeName denotes an interface, let T be the type declaration immediately enclosing the method invocation. A compile-time error occurs if there exists a method, distinct from the compile-time declaration, that overrides (§9.4.1) the compile-time declaration from a direct superclass or direct superinterface of T.

In the case that a superinterface overrides a method declared in a grandparent interface, this rule prevents the child interface from “skipping” the override by simply adding the grandparent to its list of direct superinterfaces. The appropriate way to access functionality of a grandparent is through the direct superinterface, and only if that interface chooses to expose the desired behavior. (Alternately, the developer is free to define his own additional superinterface that exposes the desired behavior with a super method invocation.)

The compile-time parameter types and compile-time result are determined as follows:

• If the compile-time declaration for the method invocation is not a signature polymorphic method, then the compile-time parameter types are the types of the formal parameters of the compile-time declaration, and the compile-time result is the result chosen for the compile-time declaration (§15.12.2.6).

• If the compile-time declaration for the method invocation is a signature polymorphic method, then:

– The compile-time parameter types are the static types of the actual argument expressions. An argument expression which is the null literal null3.10.7) is treated as having the static type Void.

– The compile-time result is determined as follows:

If the method invocation expression is an expression statement, the compiletime result is void.

Otherwise, if the method invocation expression is the operand of a cast expression (§15.16), the compile-time result is the erasure of the type of the cast expression (§4.6).

Otherwise, the compile-time result is the signature polymorphic method’s declared return type, Object.

A method is signature polymorphic if all of the following are true:

• It is declared in the java.lang.invoke.MethodHandle class.

• It takes a single variable arity parameter (§8.4.1) whose declared type is Object[].

• It has a return type of Object.

• It is native.

In Java SE 8, the only signature polymorphic methods are the invoke and invokeExact methods of the class java.lang.invoke.MethodHandle.

The following compile-time information is then associated with the method invocation for use at run time:

• The name of the method.

• The qualifying type of the method invocation (§13.1).

• The number of parameters and the compile-time parameter types, in order.

• The compile-time result, or void.

• The invocation mode, computed as follows:

– If the qualifying type of the method declaration is a class, then:

If the compile-time declaration has the static modifier, then the invocation mode is static.

Otherwise, if the compile-time declaration has the private modifier, then the invocation mode is nonvirtual.

Otherwise, if the part of the method invocation before the left parenthesis is of the form super . Identifier or of the form TypeName . super . Identifier, then the invocation mode is super.

Otherwise, the invocation mode is virtual.

– If the qualifying type of the method invocation is an interface, then the invocation mode is interface.

If the result of the invocation type of the compile-time declaration is not void, then the type of the method invocation expression is obtained by applying capture conversion (§5.1.10) to the return type of the invocation type of the compile-time declaration.

15.12.4 Run-Time Evaluation of Method Invocation

At run time, method invocation requires five steps. First, a target reference may be computed. Second, the argument expressions are evaluated. Third, the accessibility of the method to be invoked is checked. Fourth, the actual code for the method to be executed is located. Fifth, a new activation frame is created, synchronization is performed if necessary, and control is transferred to the method code.

15.12.4.1 Compute Target Reference (If Necessary)

There are six cases to consider, depending on the form of the method invocation:

• If the form is MethodName - that is, just an Identifier - then:

– If the invocation mode is static, then there is no target reference.

– Otherwise, let T be the enclosing type declaration of which the method is a member, and let n be an integer such that T is the n’th lexically enclosing type declaration of the class whose declaration immediately contains the method invocation. The target reference is the n’th lexically enclosing instance of this.

It is a compile-time error if the n’th lexically enclosing instance of this does not exist.

• If the form is TypeName . [TypeArguments] Identifier, then there is no target reference.

• If form is ExpressionName . [TypeArguments] Identifier, then:

– If the invocation mode is static, then there is no target reference. The ExpressionName is evaluated, but the result is then discarded.

– Otherwise, the target reference is the value denoted by ExpressionName.

• If the form is Primary . [TypeArguments] Identifier involved, then:

– If the invocation mode is static, then there is no target reference. The Primary expression is evaluated, but the result is then discarded.

– Otherwise, the Primary expression is evaluated and the result is used as the target reference.

In either case, if the evaluation of the Primary expression completes abruptly, then no part of any argument expression appears to have been evaluated, and the method invocation completes abruptly for the same reason.

• If the form is super . [TypeArguments] Identifier, then the target reference is the value of this.

• If the form is TypeName . super . [TypeArguments] Identifier, then if TypeName denotes a class, the target reference is the value of TypeName.this; otherwise, the target reference is the value of this.

Example 15.12.4.1-1. Target References and static Methods

When a target reference is computed and then discarded because the invocation mode is static, the reference is not examined to see whether it is null:

Click here to view code image

class Test1 {
    static void mountain() {
        System.out.println("Monadnock");
    }
    static Test1 favorite(){
        System.out.print("Mount ");
        return null;
    }
    public static void main(String[] args) {
        favorite().mountain();
    }
}

which prints:

Mount Monadnock

Here favorite() returns null, yet no NullPointerException is thrown.

Example 15.12.4.1-2. Evaluation Order During Method Invocation

As part of an instance method invocation (§15.12), there is an expression that denotes the object to be invoked. This expression appears to be fully evaluated before any part of any argument expression to the method invocation is evaluated.

So, for example, in:

Click here to view code image

class Test2 {
    public static void main(String[] args) {
        String s = "one";

        if (s.startsWith(s = "two"))
            System.out.println("oops");
    }
}

the occurrence of s before “.startsWith” is evaluated first, before the argument expression s = “two". Therefore, a reference to the string "one" is remembered as the target reference before the local variable s is changed to refer to the string "two". As a result, the startsWith method is invoked for target object "one" with argument "two", so the result of the invocation is false, as the string "one" does not start with "two". It follows that the test program does not print “oops”.

15.12.4.2 Evaluate Arguments

The process of evaluating the argument list differs, depending on whether the method being invoked is a fixed arity method or a variable arity method (§8.4.1).

If the method being invoked is a variable arity method m, it necessarily has n > 0 formal parameters. The final formal parameter of m necessarily has type T[] for some T, and m is necessarily being invoked with k ≥ 0 actual argument expressions.

If m is being invoked with kn actual argument expressions, or, if m is being invoked with k = n actual argument expressions and the type of the k’th argument expression is not assignment compatible with T[], then the argument list (e1, ..., en-1, en, ..., ek) is evaluated as if it were written as (e1, ..., en-1, new |T[]| { en, ..., ek }), where |T[]| denotes the erasure (§4.6) of T[].

The preceding paragraphs are crafted to handle the interaction of parameterized types and array types that occurs in a Java Virtual Machine with erased generics. Namely, if the element type T of the variable array parameter is non-reifiable, e.g. List<String>, then special care must be taken with the array creation expression (§15.10) because the created array’s element type must be reifiable. By erasing the array type of the final expression in the argument list, we are guaranteed to obtain a reifiable element type. Then, since the array creation expression appears in an invocation context (§5.3), an unchecked conversion is possible from the array type with reifiable element type to an array type with non-reifiable element type, specifically that of the variable arity parameter. A Java compiler is required to give a compile-time unchecked warning at this conversion. Oracle’s reference implementation of a Java compiler identifies the unchecked warning here as a more informative unchecked generic array creation.

The argument expressions (possibly rewritten as described above) are now evaluated to yield argument values. Each argument value corresponds to exactly one of the method’s n formal parameters.

The argument expressions, if any, are evaluated in order, from left to right. If the evaluation of any argument expression completes abruptly, then no part of any argument expression to its right appears to have been evaluated, and the method invocation completes abruptly for the same reason. The result of evaluating the j’th argument expression is the j’th argument value, for 1 ≤ jn. Evaluation then continues, using the argument values, as described below.

15.12.4.3 Check Accessibility of Type and Method

Let C be the class containing the method invocation, and let T be the qualifying type of the method invocation (§13.1), and let m be the name of the method as determined at compile time (§15.12.3).

An implementation of the Java programming language must ensure, as part of linkage, that the method m still exists in the type T. If this is not true, then a NoSuchMethodError (which is a subclass of IncompatibleClassChangeError) occurs.

If the invocation mode is interface, then the implementation must also check that the target reference type still implements the specified interface. If the target reference type does not still implement the interface, then an IncompatibleClassChangeError occurs.

The implementation must also ensure, during linkage, that the type T and the method m are accessible:

• For the type T:

– If T is in the same package as C, then T is accessible.

– If T is in a different package than C, and T is public, then T is accessible.

– If T is in a different package than C, and T is protected, then T is accessible if and only if C is a subclass of T.

• For the method m:

– If m is public, then m is accessible. (All members of interfaces are public9.2).)

– If m is protected, then m is accessible if and only if either T is in the same package as C, or C is T or a subclass of T.

– If m has package access, then m is accessible if and only if T is in the same package as C.

– If m is private, then m is accessible if and only if C is T, or C encloses T, or T encloses C, or T and C are both enclosed by a third class.

If either T or m is not accessible, then an IllegalAccessError occurs (§12.3).

15.12.4.4 Locate Method to Invoke

The strategy for method lookup depends on the invocation mode.

If the invocation mode is static, no target reference is needed and overriding is not allowed. Method m of class T is the one to be invoked.

Otherwise, an instance method is to be invoked and there is a target reference. If the target reference is null, a NullPointerException is thrown at this point. Otherwise, the target reference is said to refer to a target object and will be used as the value of the keyword this in the invoked method. The other four possibilities for the invocation mode are then considered.

If the invocation mode is nonvirtual, overriding is not allowed. Method m of class T is the one to be invoked.

Otherwise, if the invocation mode is virtual, and T and m jointly indicate a signature polymorphic method (§15.12.3), then the target object is an instance of java.lang.invoke.MethodHandle. The method handle encapsulates a type which is matched against the information associated with the method invocation at compile time (§15.12.3). Details of this matching are given in The Java Virtual Machine Specification, Java SE 8 Edition and the Java SE platform API. If matching succeeds, the target method encapsulated by the method handle is directly and immediately invoked, and the procedure in §15.12.4.5 is not executed.

Otherwise, the invocation mode is interface, virtual, or super, and overriding may occur. A dynamic method lookup is used. The dynamic lookup process starts from a class S, determined as follows:

• If the invocation mode is interface or virtual, then S is initially the actual run-time class R of the target object.

This is true even if the target object is an array instance. (Note that for invocation mode interface, R necessarily implements T; for invocation mode virtual, R is necessarily either T or a subclass of T.)

• If the invocation mode is super, then S is initially the qualifying type (§13.1) of the method invocation.

The dynamic method lookup uses the following procedure to search class S, and then the superclasses and superinterfaces of class S, as necessary, for method m.

Let X be the compile-time type of the target reference of the method invocation. Then:

1. If class S contains a declaration for a method named m with the same descriptor (same number of parameters, the same parameter types, and the same return type) required by the method invocation as determined at compile time (§15.12.3), then:

• If the invocation mode is super or interface, then this is the method to be invoked, and the procedure terminates.

• If the invocation mode is virtual, and the declaration in S overrides X.m8.4.8.1), then the method declared in S is the method to be invoked, and the procedure terminates.

2. Otherwise, if S has a superclass, the lookup procedure of steps 1 and 2 is performed recursively using the direct superclass of S in place of S; the method to be invoked, if any, is the result of the recursive invocation of this lookup procedure.

3. If no method is found by the previous two steps, the superinterfaces of S are searched for a suitable method.

A set of candidate methods is considered with the following properties: i) each method is declared in a (direct or indirect) superinterface of S; ii) each method has the name and descriptor required by the method invocation; iii) each method is non-static; iv) for each method, where the method’s declaring interface is I, there is no other method satisfying (i) through (iii) that is declared in a subinterface of I.

If this set contains a default method, one such method is the method to be invoked. Otherwise, an abstract method in the set is selected as the method to be invoked.

Dynamic method lookup may cause the following errors to occur:

• If the method to be invoked is abstract, an AbstractMethodError is thrown.

• If the method to be invoked is default, and more than one default method appears in the set of candidates in step 3 above, an IncompatibleClassChangeError is thrown.

• If the invocation mode is interface and the selected method is not public, an IllegalAccessError is thrown.

The above procedure (if it terminates without error) will find a non-abstract, accessible method to invoke, provided that all classes and interfaces in the program have been consistently compiled. However, if this is not the case, then various errors may occur, as specified above; additional details about the behavior of the Java Virtual Machine under these circumstances are given by The Java Virtual Machine Specification, Java SE 8 Edition.

The dynamic lookup process, while described here explicitly, will often be implemented implicitly, for example as a side-effect of the construction and use of per-class method dispatch tables, or the construction of other per-class structures used for efficient dispatch.

Example 15.12.4.4-1. Overriding and Method Invocation

Click here to view code image

class Point {
    final int EDGE = 20;
    int x, y;
    void move(int dx, int dy) {
        x += dx; y += dy;
        if (Math.abs(x) >= EDGE || Math.abs(y) >= EDGE)
            clear();
    }
    void clear() {
        System.out.println(" Point clear");
        x = 0; y = 0;
    }
}
class ColoredPoint extends Point {
    int color;
    void clear() {
        System.out.println(" ColoredPoint clear");
        super.clear();
        color = 0;
    }
}

Here, the subclass ColoredPoint extends the clear abstraction defined by its superclass Point. It does so by overriding the clear method with its own method, which invokes the clear method of its superclass, using the form super.clear().

This method is then invoked whenever the target object for an invocation of clear is a ColoredPoint. Even the method move in Point invokes the clear method of class ColoredPoint when the class of this is ColoredPoint, as shown by the output of this test program:

Click here to view code image

class Test1 {
    public static void main(String[] args) {
        Point p = new Point();
        System.out.println("p.move(20,20):");
        p.move(20, 20);

        ColoredPoint cp = new ColoredPoint();
        System.out.println("cp.move(20,20):");
        cp.move(20, 20);

        p = new ColoredPoint();
        System.out.println("p.move(20,20), p colored:");
        p.move(20, 20);
    }
}

which is:

Click here to view code image

p.move(20,20):
        Point clear
cp.move(20,20):
        ColoredPoint clear
        Point clear
p.move(20,20), p colored:
        ColoredPoint clear
        Point clear

Overriding is sometimes called “late-bound self-reference”; in this example it means that the reference to clear in the body of Point.move (which is really syntactic shorthand for this.clear) invokes a method chosen “late” (at run time, based on the run-time class of the object referenced by this) rather than a method chosen “early” (at compile time, based only on the type of this). This provides the programmer a powerful way of extending abstractions and is a key idea in object-oriented programming.

Example 15.12.4.4-2. Method Invocation Using super

An overridden instance method of a superclass may be accessed by using the keyword super to access the members of the immediate superclass, bypassing any overriding declaration in the class that contains the method invocation.

When accessing an instance variable, super means the same as a cast of this15.11.2), but this equivalence does not hold true for method invocation. This is demonstrated by the example:

Click here to view code image

class T1 {
    String s() { return "1"; }
}
class T2 extends T1 {
    String s() { return "2"; }
}
class T3 extends T2 {
    String s() { return "3"; }
    void test() {
        System.out.println("s()= "          + s());
        System.out.println("super.s()= "      + super.s());
        System.out.println("((T2)this).s()= " + ((T2)this).s());
        System.out.println("((T1)this).s()= " + ((T1)this).s());
    }
}
class Test2 {
    public static void main(String[] args) {
        T3 t3 = new T3();
        t3.test();
    }
}

which produces the output:

s()=            3
super.s()=      2
((T2)this).s()= 3
((T1)this).s()= 3

The casts to types T1 and T2 do not change the method that is invoked, because the instance method to be invoked is chosen according to the run-time class of the object referred to by this. A cast does not change the class of an object; it only checks that the class is compatible with the specified type.

15.12.4.5 Create Frame, Synchronize, Transfer Control

A method m in some class S has been identified as the one to be invoked.

Now a new activation frame is created, containing the target reference (if any) and the argument values (if any), as well as enough space for the local variables and stack for the method to be invoked and any other bookkeeping information that may be required by the implementation (stack pointer, program counter, reference to previous activation frame, and the like). If there is not sufficient memory available to create such an activation frame, a StackOverflowError is thrown.

The newly created activation frame becomes the current activation frame. The effect of this is to assign the argument values to corresponding freshly created parameter variables of the method, and to make the target reference available as this, if there is a target reference. Before each argument value is assigned to its corresponding parameter variable, it is subjected to invocation conversion (§5.3), which includes any required value set conversion (§5.1.13).

If the erasure (§4.6) of the type of the method being invoked differs in its signature from the erasure of the type of the compile-time declaration for the method invocation (§15.12.3), then if any of the argument values is an object which is not an instance of a subclass or subinterface of the erasure of the corresponding formal parameter type in the compile-time declaration for the method invocation, then a ClassCastException is thrown.

If the method m is a native method but the necessary native, implementationdependent binary code has not been loaded or otherwise cannot be dynamically linked, then an UnsatisfiedLinkError is thrown.

If the method m is not synchronized, control is transferred to the body of the method m to be invoked.

If the method m is synchronized, then an object must be locked before the transfer of control. No further progress can be made until the current thread can obtain the lock. If there is a target reference, then the target object must be locked; otherwise the Class object for class S, the class of the method m, must be locked.

Control is then transferred to the body of the method m to be invoked. The object is automatically unlocked when execution of the body of the method has completed, whether normally or abruptly. The locking and unlocking behavior is exactly as if the body of the method were embedded in a synchronized statement (§14.19).

Example 15.12.4.5-1. Invoked Method Signature Has Different Erasure Than Compile-Time Method Signature

Consider the declarations:

Click here to view code image

abstract class C<T> {
    abstract T id(T x);
}
class D extends C<String> {
    String id(String x) { return x; }
}

Now, given an invocation:

Click here to view code image

C c = new D();
c.id(new Object());  // fails with a ClassCastException

The erasure of the actual method being invoked, D.id(), differs in its signature from that of the compile-time method declaration, C.id(). The former takes an argument of type String while the latter takes an argument of type Object. The invocation fails with a ClassCastException before the body of the method is executed.

Such situations can only arise if the program gives rise to a compile-time unchecked warning (§4.8, §5.1.9, §5.5.2, §8.4.1, §8.4.8.3, §8.4.8.4, §9.4.1.2, §15.12.4.2).

Implementations can enforce these semantics by creating bridge methods. In the above example, the following bridge method would be created in class D:

Click here to view code image

Object id(Object x) { return id((String) x); }

This is the method that would actually be invoked by the Java Virtual Machine in response to the call c.id(new Object()) shown above, and it will execute the cast and fail, as required.

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

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