Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4485402

Wrong implementation of definition of "override"

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P5 P5
    • 1.4.0
    • 1.3.1
    • specification
    • 1.4
    • generic
    • generic
    • Not verified



      Name: bsC130419 Date: 07/27/2001


      java version "1.3.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build Blackdown-1.3.0-FCS)
      Java HotSpot(TM) Client VM (build Blackdown-1.3.0-FCS, mixed mode)

      but also:

      java version "1.1.6"


      Hello,

      I encountered some inconsistency in the Java Language Specification, and want to
      discuss some solutions, if the problem is not already well known.

      During my ongoing effort to formalize the Java package/access concept in the
      theorem prover Isabelle, I noticed some discrepancy between the
      Java Language Specification (Second Edition) and the implementation of Java in
      the JDK (version 1.3.0). Strictly speaking the implementation is wrong with
      respect to the specification.
      The problem arises in the combination of inheritance/overriding and access
      control.
      First I will cite the relevant parts of the Java Language Specification and
      then give a short example to illustrate the discrepancy. Finally I
      propose an addition to the Java Language Specification which will hopefully
      solve the problem.

      ----------- Java Language Specification---------------------------------------
        8.4.6.1 Overriding (by Instance Methods)
        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
           a) m2 is non-private and accessible from C, or
           b) m1 overrides a method m3, m3 distinct from m1, m3 distinct from m2,
              such that m3 overrides m2.
      ------------------------------------------------------------------------------

      So to decide whether one method overrides another we need to know more about
      accessibility:

      ----------- Java Language Specification---------------------------------------
        6.6.1 Determining Accessibility
        A package is always accessible. If a class or interface type is declared
        public, then it may be accessed by any code, provided that the compilation
        unit (?7.3) in which it is declared is observable. If a top level class or
        interface type is not declared public, then it may be accessed only from
        within the package in which it is declared. An array type is accessible
        if and only if its element type is accessible.
        
        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:
           If the member or constructor is declared public, then access is
      permitted.
           All members of interfaces are implicitly public.

           Otherwise, if the member or constructor is declared protected, then
      access
           is permitted only when one of the following is true:
              
               Access to the member or constructor occurs from within the package
               containing the class in which the protected member or constructor is
               declared.

               Access is correct as described in ?6.6.2.

           Otherwise, if the member or constructor is declared private, then access
           is permitted if and only if it occurs within the body of the top level
           class (?7.6) that encloses the declaration of the member.

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

      So for a member to be accessible it is necessary that the class of the member
      is accessible.

      Consider the following Java Program:
         
      package APackage;
      class A
      {
          public void foo(){System.out.println("A.foo called");}
      }

      package APackage;
      public class AS extends A
      {
          public void call_fooA(A a) {
      System.out.println("AS.call_fooA called; calling foo");
      a.foo();
          }
          public void call_fooAS(AS as) {
      System.out.println("AS.call_fooAS called; calling foo");
      as.foo();
          }
      }

      package BPackage;
      import APackage.AS;
      public class B extends AS
      {
          public void foo(){System.out.println("B.foo called");}
      }

      package Main;
      import APackage.AS;
      import BPackage.B;
      public class InvisibleInheritance
      {
          public static void main(String args[])
          {
      B b = new B();
              AS as = new AS();
              System.out.println("calling as.call_fooAS(b)");
              as.call_fooAS(b);
              System.out.println("calling as.call_fooA(b)");
              as.call_fooA(b);
          }
      }

      Class A is not declared public, so it is accessible only from within its
      package (APackage). A declares a public method foo().

      Class AS extends A and is declared public. So it can also be accessed from
      outside of the package APackage. AS inherits the method foo() from A.

      Class B is defined in a different package than A and AS. So it can access AS
      but
      not A. Hence it can access AS.foo() but not A.foo(). B declares a new
      method foo() with the same signature as A.foo().
        
        What about overriding?

      First of all I want to clarify the notion of "declare" I will use. The exact
      meaning of "declare" isn't defined explicitely in the Java Language
      Specification. But it implicitely gets clear if we look at the following phrase
      of the specification:
      ----------- Java Language Specification---------------------------------------
      8.2 Class Members

      The members of a class type are all of the following:

       Members inherited from its direct superclass (?8.1.3), except in class Object,
       which has no direct superclass

       Members inherited from any direct superinterfaces (?8.1.4)

       Members declared in the body of the class (?8.1.5)
      ------------------------------------------------------------------------------

      So if a Class X "declares" a method m this means to me, that m is
      declared in the body of the class itsself, it is not an inherited method!

      Strictly referring to the language specification we have
      the following situation:

        B.foo() doesn't override A.foo(), since
          1. B is a subclass of A
          but neither 2 a) nor 2 b) holds.
            2 a) A.foo() is not accessible from B, since A is not accessible from B
                 Remark: The Rule 2 a) doesn't give us the freedom to say that
                         A.foo() is accessible, because AS inherits it, and AS.foo()
                         is accessible from B! There is no word about subclasses or
                         inheritance in this rule.
            2 b) there is no intermediate method that overrides A.foo() which is
                 overridden by B.foo(). (AS.foo() is just inherited from A.foo(),
                 it does not override A.foo()).

        B.foo() doesn't override AS.foo(), since it is senseless to talk of the
        term "override" in this case, since no method foo() is declared in AS (it
        is just inherited from A)
       
      Therefor the calls to as.callFooAS(b) and as.callFooA(b) should lead to the
      same result. They should both call A.foo():
      callFooAS(b):
        Inside of this method the expected static type of the parameter is AS,
        the dynamic type is B. So calling foo should invoke A.foo(), since B.foo()
        doesn't override A.foo()
      callFooA(b):
        Inside of this method the expected static type of the parameter is A,
        the dynamic type is B. So calling foo should invoke A.foo(), since B.foo()
        doesn't override A.foo().

      The Java compiler (for Java version 1.3.0) has a different opinion about this.
      Executing the program gives us the following output:

      calling as.call_fooAS(b)
      AS.call_fooAS called; calling foo
      B.foo called
      calling as.call_fooA(b)
      AS.call_fooA called; calling foo
      B.foo called

      In both cases B.foo is called, so the current implementation considers
      B.foo() to override A.foo().

      The concepts of classes/inheritance and packages/access doesn't seem to fit
      together very well in this special situation. The origin of the problem is, that
      the method A.foo() is defined with public access but the class A itself is
      only defined with package access. The method becomes visible due to inheritance
      in the class AS. The problem would not occur if the method A.foo() were
      defined with default (package) access. Then it has to be overridden in the
      class AS, by a method foo() with public access, to be visible from
      outside the package. Let's look at these modifications to the method
      declarations in more detail:

        A.foo() (* default package access *)
        public AS.foo()
        public B.foo()

      AS.foo() overrides A.foo:
        1.AS is a subclass of A
        2 a) A.foo() is non-private and accessible from AS, since AS and A are
             inside the same package.

      But now B.foo() also overrides A.foo(), because:
          1.B is a subclass of A
          2 b) there is the intermediate method AS.foo().
               B.foo() overrides AS.foo(), AS.foo() distinct from B.foo(),
               AS.foo() distinct from A.foo and AS.foo() overrides A.foo().
       
      I think this is the reason for the rule 2 b) of overriding in the Java
      Language Specification.

      The problem seems to be known to the language designers, since
      in JavaCard this problematic situation is avoided by a constraint which
      enforces the just described modifications.
      Concerning the JavaCard 2.1.1 Virtual Machine Specification page 9,
      "a package-visible class that is extended by a public class cannot define
      any public or protected methods".

      In principle there are three solutions to remove the inconsistency.
       1. Implement Java according to its specification.
       2. Restrict the access control as in case of JavaCard.
       3. Adjust the specification to fit the implementation.

      Solutions 1 and 2 seem to be utopian, since Java is widely spread and used with
      the "de facto" standard semantics of the implementation.

        To be realistic I think the Java Language Specification should be adjusted to
      the "de facto" standard, defined in the JDK. (Thats real life: The
      specification
      must be adjusted to fit the implementation!). So I want to discuss some
      proposal to fix the definition of overriding:

        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
           a) m2 is non-private and accessible from C, or
           b) m1 overrides a method m3, m3 distinct from m1, m3 distinct from m2,
              such that m3 overrides m2, or
           c) There is an intermediate class B, C is a subclass of B and B is a
              subclass of A, so that m1 is inherited by B and m1 gets accessible
              from C through to this inheritance (B.m1 is accessible
              from C).

      Norbert Schirmer
      (Review ID: 128958)
      ======================================================================

            gbrachasunw Gilad Bracha (Inactive)
            bstrathesunw Bill Strathearn (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: