-
Bug
-
Resolution: Fixed
-
P4
-
1.3.0
-
hopper
-
generic
-
generic
-
Verified
Name: stC104175 Date: 08/14/2000
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0)
Classic VM (build 1.3.0, J2RE 1.3.0 IBM build cx130-20000623 (JIT enabled:
jitc))
The behavior of compiler with regards to method inheritance and overriding has
changed between various JDKs, and illustrates an ambiguity in the JLS that needs
to be clarified.
=========
package secure;
public abstract class A {
public static void main(String[] args) {
A a = new C();
System.out.println(a.message());
}
abstract String message();
}
class C extends other.B {
String message() { return "Hello, world"; }
}
=========
package other;
public abstract class B extends secure.A {
}
=========
=========
Compiled under JDK1.1.8:
(no errors)
output of java secure.A:
Hello, world
output of java other.B:
Hello, world
output of java secure.C:
Hello, world
=========
Compiled under JDK1.2.2:
secure/A.java:5: class secure.C is an abstract class. It can't be instantiated.
System.out.println(new C().message());
^
secure/A.java:11: class secure.C must be declared abstract. The package-private
abstract method java.lang.String message() in superclass class secure.A is
inaccessible and cannot be overridden.
class C extends other.B {
^
2 errors
(other/B.class exists after compilation)
output of java other.B:
Exception in thread "main" java.lang.NoClassDefFoundError: secure/A
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:474)
at
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:106)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:247)
at java.net.URLClassLoader.access$1(URLClassLoader.java:215)
at java.net.URLClassLoader$1.run(URLClassLoader.java, Compiled Code)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java, Compiled Code)
at java.lang.ClassLoader.loadClass(ClassLoader.java, Compiled Code)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java, Compiled
Code)
at java.lang.ClassLoader.loadClass(ClassLoader.java, Compiled Code)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java, Compiled
Code)
==========
Compiled under JDK1.3:
1) Compiling only secure/A.java (with C defined in A.java), compiling
secure/A.java and other/B.java together (with C defined in A.java):
secure/A.java:11: secure.C should be declared abstract; it does not define
message() in secure.A
class C extends other.B {
^
1 error
(secure/A.class exists after compilation)
output of java secure.A:
Exception in thread "main" java.lang.NoClassDefFoundError: secure/C
at secure.A.main(A.java:5)
2) Compiling only other/B.java (with C defined in A.java):
./secure/A.java:11: cyclic inheritance involving other.B
class C extends other.B {
^
1 error
(no classfiles exist after compilation)
3) Compiling only other/B.java (where C is in C.java):
./secure/C.java:3: secure.C should be declared abstract; it does not define
message() in secure.A
class C extends other.B {
^
1 error
(both secure/A.class and other/B.class exist)
output of java secure.A:
Exception in thread "main" java.lang.NoClassDefFoundError: secure/C
at secure.A.main(A.java:5)
output of java other.B:
Exception in thread "main" java.lang.NoClassDefFoundError: secure/C
at secure.A.main(A.java:5)
4) compiling only secure/C.java (with C defined in C.java):
secure/C.java:3: secure.C should be declared abstract; it does not define
message() in secure.A
class C extends other.B {
^
1 error
(no classfiles exist after compilation)
==========
Compiled under jikes (Version 1.12 8/1/2000):
(no errors)
output, from the JVM of any JDK (1.1.8, 1.2.2, or 1.3), of secure.A, other.B,
and secure.C:
Hello, world
==========
The appearance of that cyclic inheritance comment under compilation 2 with JDK
1.3 relates to bug 4326631.
One question is why JDK 1.2.2 and JDK 1.3 generate classfiles when the
compilation fails in an error. If I change line 5 of A to reference a
non-existant type D, no classfiles are generated.
The real question, though, is the proper interpretation of the JLS.
Section 6.6.1:
"A member (class, interface, field, or method) of a reference (class, interface,
or array) type or a constructor of a class type is accessible only if the type
is accessible and the member or constructor is declared to permit access:
...
" Otherwise, we say there is default access, which is permitted only
when the access occurs from within the package in which the type is declared. "
Section 8.1.3:
"The subclass relationship is the transitive closure of the direct subclass
relationship. A class A is a subclass of class C if either of the following is
true:
" A is the direct subclass of C.
" There exists a class B such that A is a subclass of B, and B is a
subclass of C, applying this definition recursively. "
Section 8.4.6.1:
"An instance method m1 declared in a class C overrides another method with the
same signature, m2, declared in class A if both
" 1.C is a subclass of A.
" 2.Either
m2 is non-private and accessible from C, or
m1 overrides a method m3, m3 distinct from m1, m3 distinct from m2,
such that m3 overrides m2.
"Moreover, if m1 is not abstract, then m1 is said to implement any and all
declarations of abstract methods that it overrides."
I take this to mean that secure.C is a subclass of secure.A (by 8.1.3), the
abstract method A.message is accessible from C as it is in the same package (by
6.6.1), and therefore, C.message satisfies the first half of 8.4.6.1 condition
2. And, since C.message is not abstract, it should implement A.message, and the
class C need not be abstract. This appears to be the interpretation of
JDK1.1.8, as well as jikes. And, the VM of JDK1.3 does not throw any exceptions
when running this code; so it passes verification.
If this interpretation is wrong, then there is a specification bug, and 8.4.6.1
needs to add another condition that all intermediate superclasses between C and
A must be able to access m2 or something that overrides m2. In my example, this
condition does not hold, as other.B is unable to access or override A.message,
and is an intermediate superclass between C and A, explaining the error message
given during compilation.
If you do change the specification, you may consider making it a compile-time
error to extend a class that contains abstract methods that you are unable to
implement. Using JDK 1.3, there is no way that I can make other.B or any
extension thereof implement the method A.message; so there is no point in being
able to compile B except for its class (static) methods.
(Review ID: 108262)
======================================================================
- relates to
-
JDK-4642788 8.4.6.1 Overriding is unclear.
- Closed
-
JDK-4686811 package private methods do not override across package boundary
- Closed