13.4.5 Class Body and Member Declarations

No incompatibility with pre-existing binaries is caused by adding a class member that has the same name (for fields) or same name, signature, and return type (for methods) as a member of a superclass or subclass. References to the original field or method were resolved at compile time to a symbolic reference containing the name of the class in which they were declared. This makes compiled Java code more robust against changes than it might otherwise be. No error occurs even if the set of classes being linked would encounter a compile-time error. As an example, if the program:

class Hyper { String h = "Hyper"; }
class Super extends Hyper { }
class Test extends Super {
	public static void main(String[] args) {
		String s = new Test().h;
		System.out.println(s);
	}
}

is compiled and executed, it produces the output:

Hyper

Suppose that a new version of class Super is then compiled:

class Super extends Hyper { char h = 'h'; }

If the resulting binary is used with the existing binaries for Hyper and Test, then the output is still:

Hyper

even though compiling the source for these binaries:

class Hyper { String h = "Hyper"; }
class Super extends Hyper { char h = 'h'; }
class Test extends Super {
	public static void main(String[] args) {
		String s = new Test().h;
		System.out.println(s);
	}
}

would result in a compile-time error, because the h in the source code for main would now be construed as referring to the char field declared in Super, and a char value can't be assigned to a String.

Deleting a class member or constructor that is not declared private may cause a linkage error if the member or constructor is used by a pre-existing binary, even if the member was an instance method that was overriding a superclass method. This is because, during resolution, the linker looks only in the class that was identified at compile time. Thus, if the program:


class Hyper {
	void hello() { System.out.println("hello from Hyper"); }
}

class Super extends Hyper {
	void hello() { System.out.println("hello from Super"); }
}

class Test {
	public static void main(String[] args) {
		new Super().hello();
	}
}

is compiled and executed, it produces the output:

hello from Super

Suppose that a new version of class Super is produced:

class Super extends Hyper { }

If Super and Hyper are recompiled but not Test, then a NoSuchMethodError will result at link time, because the method hello is no longer declared in class Super.

To preserve binary compatibility, methods should not be deleted; instead, "forwarding methods" should be used. In our example, replacing the declaration of Super with:


class Super extends Hyper {
	void hello() { super.hello(); }
}

then recompiling Super and Hyper and executing these new binaries with the original binary for Test, produces the output:

hello from Hyper

as might have naively been expected from the previous example.

The super keyword can be used to access a method declared in a superclass, bypassing any methods declared in the current class. The expression:

super.Identifier

is resolved, at compile time, to a method M declared in a particular superclass S. The method M must still be declared in that class at run time or a linkage error will result. If the method M is an instance method, then the method MR invoked at run time is the method with the same signature as M that is a member of the direct superclass of the class containing the expression involving super. Thus, if the program:


class Hyper {
	void hello() { System.out.println("hello from Hyper"); }
}
class Super extends Hyper { }
class Test extends Super {

	public static void main(String[] args) {
		new Test().hello();
	}

	void hello() {
		super.hello();
	}
}

is compiled and executed, it produces the output:

hello from Hyper

Suppose that a new version of class Super is produced:


class Super extends Hyper {
	void hello() { System.out.println("hello from Super"); }
}

If Super and Hyper are recompiled but not Test, then running the new binaries with the existing binary of Test produces the output:

hello from Super

as you might expect. (A flaw in some early versions of Java caused them to print:

hello from Hyper

incorrectly.)