Name: mc57594 Date: 01/27/97
The correction of bug 4023931 is not correct because it
implements a tricky exception to avoid that a package-private method can be
overriden by methods defined in subclasses belonging to other packages. But
the very reason of the problem is still there: javac, in violation of the
Java specifications (8.2 and 6.4.2), considers that package-private class
members are well inheritable by subclasses of other packages, while they are
not visible within these subclasses.
Bug 4023931 was a logical consequence of this violation of the rule
forbiding the inheritance of package-private members outside the package
where they are defined: it resulted from the application of the usual
overriding mechanism for inherited methods in this case. The present
correction of the bug is dangerous: to correct the bug without correcting
its real cause, javac allows an exception to the overriding mechanism, and
this leads to inconsistencies and several other violations of the
specifications.
Example (the 4 classes are defined in 4 different source files):
package motherpack;
public class Mother {
public Mother(){}
String meth() {return "mother meth";}}
package daughterpack;
import motherpack.Mother;
public class Daughter extends Mother {
public Daughter(){}}
package motherpack;
import daughterpack.Daughter;
public class GrandDaughter extends Daughter {
public GrandDaughter(){}}
import java.io.*;
import daughterpack.Daughter;
public class Main {
public static void main(String[] args) {
GrandDaughter obj = new GrandDaughter
System.out.println(obj.meth());
System.out.println(((Daughter)obj).meth());
System.out.println(((Mother)obj).meth());}}
This program is successfully compiled by javac and prints:
mother meth
mother meth
mother meth
According to Java specifications:
- 6.4.2: "The only members of a class are those inherited from its
direct superclasse and its direct superinterfaces and those defined in its
body."
- 8.2: "Only members of a class that are declared protected or
public are inherited by subclasses declared in a package other than the one
in which the class is declared."
In the example program above, in the member access expressions obj.meth()
and ((Daughter)obj).meth(), the type of the variable is respectively
GrandDaughter and Daughter. From 6.4.2 and 8.2, it comes that meth() is not
a member of these reference types. So these method access expressions are
illegal and should be rejected by the compiler: only the
expression((Mother)obj).meth() should be accepted. If the compiler accepts
the (illegal) expressions obj.meth() and ((Daughter)obj).meth(), it is
because it considers (in violation of 8.2) the package-private members of
Mother as inherited by Daughter, while accessible only from the package of
Mother (and not even within the body of the class Daughter) and then
inherited from Daughter by GrandDaughter. This violation leads to cumbersome
confusions, as illustrated below.
Now, suppose that we rewrite the class Daughter like this:
package daughterpack;
import motherpack.Mother;
public class Daughter extends Mother {
public Daughter(){}
String meth(){return "Daughter meth" ;}}
The result of our main program does not change (the new method is invisible
outside the daughterpack package). But this leads to a first problem: the
access expression ((Daughter)obj).meth() then does not denote the same
method according to the context where it occurs: within package motherpack
it returns the value "mother meth" while within daughterpack it returns
"daughterpack meth! This is a fitrst violation of the specifications.
Now, suppose that instead we rewrite the class Daughter like this:
package daughterpack;
import motherpack.Mother;
public class Daughter extends Mother {
public Daughter(){}
public int meth(){return 666 ;}}
Now in JDK 1.1 beta 3 this is allowed. But there happens an indetermination:
on a variable obj of type GrandDaughter, what will denote the expression
obj.meth() within package motherpack: the String method indirectly (and in
violation of specs) inherited from Mother or the int one inherited from
Daughter? There is an ambiguity, which is not directly seen by javac: here,
in practice, it apparently gives priority to the method inherited from
Daughter, and runing main gives:
666
666
mother meth
But suppose that one wants also then to rewrite the class GrandDaughter as
follows:
package motherpack;
import daughterpack.Daughter;
public class GrandDaughter extends Daughter {
public GrandDaughter(){}
public int meth() {return 777;}}
In this case the method would override the one it inherits from Daughter.
But, there javac detects a problem and reject this definition because it
finds the return type of GrandDaughter.meth() (int) incompatible with the
return type of Mother.meth() (String)!!!!!!
If then one tries alternatively:
package motherpack;
import daughterpack.Daughter;
public class GrandDaughter extends Daughter {
public GrandDaughter(){}
public String meth() {return "granddaughter meth";}}
javac now rejects also this definition because it finds the return type of
GrandDaughter.meth() (now String) incompatible with the return type of
Daughter.meth() (int) in this case!!!!
Now suppose we redefine Daughter and GrandDaughter as follows:
package daughterpack;
import motherpack.Mother;
public class Daughter extends Mother {
public Daughter(){}
public String meth(){return "daughter meth" ;}}
and
package motherpack;
import daughterpack.Daughter;
public class GrandDaughter extends Daughter {
public GrandDaughter(){}
public String meth() {return "granddaughter meth";}}
javac then finds no problem. But running our main program leads to find an
inconsistent behaviour of java, as it prints:
granddaughter meth
daughter meth
granddaughter meth
Clearly the method of GrandDaughter has overriden the one of Mother but not
the one of Daughter!!!! This is a bug.
All those troubles and bugs would have been avoided if specs 6.4.2 and 8.2
had been respected...
Note also that the problem is not specific to package-private methods (and
also attributes). It is also possible to show the same kind of violation
with class-private methods (and attributes). Javac considers them also as
inherited by subclasses (while visible on these subclasses only with the
body of the class where they are defined).
In conclusion, instead of respecting inheritance exceptions clearly
documented in Java official specifications (6.4.2 and 8.2), javac considers
that all methods and attributes are inherited, which leads to manage tricky
exceptions to the usual overriding/hiding rules when these inherited members
are not visible from the inheriting classes, tricky exceptions that are hard
to understand and implement in a consistent way, and makes Java implemented
semantics dangerously different in practice from the one officially
specified by JavaSoft.
======================================================================
ken.arnold@East 1997-03-21
I've been playing around with an independent report of this same bug, and it seems as if we are hacking around a VM deficiency, so cannot fix the real problem. When I invoke a package-accessible method, the invoke_virtual has no way of knowing that I intend to invoke only a package-accessible one. The critical information has been lost. I think we need a new VM instruction, something like invoke_package_virtual. You cannot intuit this from the execution environment. We are giving partial inheritiance if inaccessible methods which violates JLS.
ken.arnold@East 1997-03-21
- duplicates
-
JDK-4026584 package-private attributes and methods are inherited outside the
-
- Closed
-
- relates to
-
JDK-4040517 Bogus warning generated for "override" of inaccessible method
-
- Closed
-
-
JDK-4248990 Method lookup inconsistent with classic VM behavior
-
- Closed
-