How is the method invocation related to its definition?
As we stated in the introduction, defining a method doesn’t actually cause it to be executed; instead, when we define a method, we are “teaching” the computer—more correctly, adding this method to the capabilities of the class in which the method is defined, or to the instances of that class. It is only when the method is invoked that it is executed.
However, before a method can be invoked at runtime, the definition of the method must be compiled, along with its invocations. When a method invocation is compiled, the Java compiler compares the invocation to the compiled method declaration, to make sure that the invocation will be valid at runtime. In order to write Java code effectively, we need to understand how this comparison is performed.
The syntax of the method definition was already introduced in “Definition”. Now, let’s take a look at the invocation syntax:
[contextQualifier.]methodName([argument[, …]])
(Note that this does not show assignment of a method’s return value to a variable, or use of that return value in some other expression. At runtime, that assignment or other use takes place after the invoked method has completed processing; it is not actually part of the invocation syntax. However, when the value returned from a method invocation is used in a surrounding expression, that use will also be checked during compilation, for compatibility between the return type of the method and the type expected in the surrounding expression.)
contextQualifier
An instance (non-static
) method must be invoked in the context of an object reference, whose reference type is the class (or interface) in which the method is declared, or a subclass (or subinterface) of that.
In an earlier example, we had this invocation of the convertC2F
instance method, in the second line of a code fragment that also included creation of an instance of the Temperature
class, followed by a declaration-with-assignment statement:
Temperature t = new Temperature();
double f = t.convertC2F(100);
Here, the context qualifier is the instance of Temperature
referenced by the variable t
.
A static
method is invoked in the context of the class or interface (or a subclass or subinterface of that) in which the method is defined; thus, an appropriate value for this placeholder is a class name.
In the static
version of the convertC2F
example, we had this invocation of a static
method, as part of a declaration-with-assignment statement:
double f = Temperature.convertC2F(100);
Here, the context qualifier is the Temperature
class itself.
If method A
is being invoked from another method, B
, and the context in which B
was invoked is the same one in which A
should be invoked, the context qualifier may be omitted. In practical terms, this means that when invoking a static
method from a static
or instance method in the same class, or when invoking an instance method from an instance method in the same class, we can usually leave the context qualifier out.
methodName
The name of the method to be invoked; this must match the declared method name exactly.
argument
An expression which will be evaluated prior to invocation, with the resulting value passed to the method being invoked. Any arguments must match the declared parameters of the method being invoked—that is, each argument’s type must be compatible with the type of the corresponding parameter, without skipping or reordering any of the arguments or parameters. (Java does not support optional parameters: every parameter in a method declaration must be matched by a corresponding argument in the invocation, or the invocation will not compile.)
If the method being invoked has no declared parameters, the invocation must similarly include no arguments—but the parentheses are still required.
When a method definition—or just the declaration, for an abstract method—is compiled, the resulting bytecode contains the method signature (the combination of method name and parameter types, order, and number), along with the other elements of the method declaration (modifiers, return type, and throws
clause, if any). Of course, the method implementation (if any) is compiled into the bytecode—but it is the declaration, not the implementation, that is used in when compiling any invocations of the method.
As noted above, an invocation of a method must match the declaration of an accessible method in the class specified by the invocation’s context qualifier. For example, if the invocation context is a class name, then the invocation must match a static
method defined in that class, or defined in a superclass or implemented interface (or superinterface) of it. On the other hand, if the invocation context is a reference to an instance of a given class, then the invocation must match an instance or static
method declared in that class (or in a superclass or implemented interface). If a match is not found, compilation of the invocation fails.
Here, we have a statement that successfully compiles to invoke the static
method atan2
, defined in the Math
class.
double theta = Math.atan2(-1.0, 1.0);
The invocation context (the Math
class) matches the class where the method is defined.
Each of the arguments’ types (both are double
) is compatible with the type of the corresponding parameter (also double
) in the method declaration.
The type of theta
(the assignment target) is double
, which is compatible with the declared return type (again, double
) of the atan2
method.
On the other hand, we have an invocation of the charAt
method, defined in the String
class, that doesn’t compile:.
char c = "bootcamp".charAt(5.0);
There is a method named charAt
defined in the String
class. (So far, so good.)
charAt
is an instance (non-static
) method, and the literal value "bootcamp"
is an instance of the String
class, so the invocation context is correct.
The return type of charAt
is char
, which matches the type of the assignment target, c
.
However, the charAt
method is declared with a parameter of type int
, and the argument 5.0
is a literal value of type double
—which is not assignment-compatible with the int
type of the parameter.
The problem shown above can be resolved by using a literal int
value (or any expression that’s assignment-compatible to an int
) as an argument to the charAt
method. For example, the following invocation will compile (and execute) successfully:
char c = "bootcamp".charAt(5);