Java 包私有访问级别的不一致行为

Java 包私有访问级别的不一致行为,java,Java,我知道包私有(默认)修饰符应该为每个类隐藏一个方法(或成员),但同一个包中的类除外。但是,我发现这个访问级别存在一些问题 考虑以下代码: // pack1/MyBaseClass.java package pack1; public class MyBaseClass { /* package private */ void someMethod() { System.out.println("MyBaseClass.someMethod"); } } //

我知道包私有(默认)修饰符应该为每个类隐藏一个方法(或成员),但同一个包中的类除外。但是,我发现这个访问级别存在一些问题

考虑以下代码:

// pack1/MyBaseClass.java
package pack1;

public class MyBaseClass {
    /* package private */ void someMethod() {
        System.out.println("MyBaseClass.someMethod");
    }
}


// pack1/MyDerivedClassFromOtherPackage.java
package pack1;

import pack2.MyDerivedClassInAnotherPackage;

public class MyDerivedClassFromOtherPackage extends MyDerivedClassInAnotherPackage {
    @Override
    /* package private */ void someMethod() {
        System.out.println("MyDerivedClassFromOtherPackage.someMethod");
        // super.someMethod();  // can't compile
    }
}


//pack2/MyDerivedClassInAnotherPackage.java
package pack2;

import pack1.MyBaseClass;

public class MyDerivedClassInAnotherPackage extends MyBaseClass {
    // @Override // can't do this (compile error)
    /* package private */ void someMethod() {
        System.out.println("MyDerivedClassInAnotherPackage.someMethod");
    }
}
因此,包pack1中基本上有一个带有包私有方法(MyBaseClass)的基类。pack2中的另一个类(MyDerivedClassInOnotherPackage)扩展了MyBaseClass,并且它不能覆盖包私有方法(到目前为止还可以:包私有方法只能在同一个包中访问)。 但是,pack1中的另一个类扩展了pack2.MyderivedClassInotherPackage,这次它可以重写其包私有方法(但它不能调用
super.someMethod()

另一件奇怪的事情是,某些方法对
pack1.MyDerivedClassFromOtherPackage
而不是
pack2.myderivedclassnotherpackage
的行为是多态的。下面的代码应该清楚地说明我的意思:

package pack1;

import pack2.MyDerivedClassInAnotherPackage;

public class MainClass {

    public static void main(String[] args) {
        MyBaseClass bc = new MyBaseClass();
        bc.someMethod();
        System.out.println("-------------------------------------------------");

        MyBaseClass otherpack = new MyDerivedClassInAnotherPackage();
        otherpack.someMethod();
        System.out.println("-------------------------------------------------");

        MyBaseClass samepack = new MyDerivedClassFromOtherPackage();
        samepack.someMethod();
        System.out.println("-------------------------------------------------");

    }

}
输出:

MyBaseClass.someMethod
-------------------------------------------------
MyBaseClass.someMethod
-------------------------------------------------
MyDerivedClassFromOtherPackage.someMethod
-------------------------------------------------
与这些行为有关的问题:

1. How come that pack1.MyDerivedClassFromOtherPackage is allowed to override the (package-private) method of pack2.MyDerivedClassInAnotherPackage?
2. In spite of this, why is it not possible to call super.someMethod() from pack1.MyDerivedClassFromOtherPackage?
3. Why is the class in the same package (pack1) behaving polymorphically and the one in the other package (pack2) not?
PS1:我知道我应该一次发布一个问题,但我觉得这三个问题一定有相同的根本原因。(我怀疑实际上pack1.MyDerivedClassFromOtherPackage.someFunction覆盖了pack1.MyBaseClass.someFunction,而不是pack2.myderivedclassnotherpackage.someFunction。但这仍然无法解释第二个问题,而且我也无法确认。)

PS2:对于这个场景,我没有任何实际的用例。我只是在玩一些小游戏(为OCA考试做准备)。

1)您在这里覆盖的不是MyDerivedClassInotherPackage#someMethod,而是MyBaseClass#someMethod()。由于包的私密性,第一个被跳过,编译器将正确地将重写的级别解析为MyBaseClass,因为您的子类与它位于同一个包中

请注意,假设我们从层次结构中的第二个类中删除@Override注释,那么这一点就成立了,否则您的代码甚至无法编译

这里有趣的后续问题是,为什么注释系统仍然抛出错误,而代码在本例中编译得很好

2) 因为super解析为立即超类,在本例中,您无法访问该超类的方法

3) 编辑:正如BroncoAbiertos回答中指出的,这仅仅是因为这是唯一一个方法被实际重写的地方。实际上,您只能通过从第二个类中删除@Override注释(如上面的答案1所示)来编译它,否则您的代码将无法编译

  • 您不能重写pack2.MyDerivedClassInotherPackage中的someMethod,因为您将其声明为pack2的包私有。也就是说,您正在消除对pack1的访问,从而限制其可见性,这在重写方法时是非法的。当您从pack1.MyDerivedClassFromOtherPackage覆盖它时,可见性保持不变,这是合法的
  • 您无法调用super.someMethod(),因为您正在尝试访问pack2.myderivedClassInotherPackage.someMethod(),但无法从pack1访问pack2.myderivedClassInotherPackage方法
  • 这个方法只在pack1.MyDerivedClassFromOtherPackage中被重写,所以这并不奇怪

  • 您是否尝试过用
    public
    修饰符覆盖pack2.myderivedClassInotherPackage中的某个方法?@broncoAbierto:我不明白您的建议。如果我这样做,它将如何帮助我理解包私有(默认)访问级别的行为。(这就是问题的目的。)我猜您不能重写,因为通过将其可见性声明为pack2包私有,您限制了其可访问性。@RKC:谢谢您的改进。不过,我不明白最后一个问题:为什么你要把我的问题写成代码?@broncoAbierto:好吧,但为什么它让我在这样的情况下打开覆盖注释?(参见第一个问题。)关于第一个答案,我认为它是编译的,因为它没有覆盖父方法。但是,如果您添加了重写注释,您会告诉编译器确保您正在重写某些内容,这就是为什么它不会编译。是的,但我考虑的是层次结构中第三个类中的注释。如果只保留该注释,并删除第二个注释,代码将编译,但注释处理器仍会抱怨错误。感谢你们两位的澄清。两个答案在我看来都是正确的,但不幸的是我只能接受一个。。。