【问题标题】:Why is package-protected method not visible in the same package?为什么包保护的方法在同一个包中不可见?
【发布时间】:2018-03-24 21:01:30
【问题描述】:

假设我们有两个包p1p2 以及由p2.M12 扩展的类p1.M1,如下所示:

package p1;

public class M1 {
    void method1() {
        System.out.println("Method 1 called");
    }
}


package p2;

import p1.M1;

public class M12 extends M1 {
    void method2() {
        System.out.println("Method 2 called");
    }
}

让我们将M12 扩展为p2.B

package p2;

public class B extends M12 {

    public void doSomething()  {
        method1();
        method2();
    }
} 

这会导致编译错误为method1,在p1 中受包保护在p2 中不可见。 method2 是可见的,没有问题。

现在让我们用p1.A 扩展p2.M12

package p1;

import p2.M12;

public class A extends M12 {

    public void doSomething() {
        method1();
        method2();
    }
}

这里我遇到了method2()(这是可以理解的)method1()的编译错误:

我的问题是:为什么包p1 中受包保护的method1 在来自同一包p1 的类A 中不可见?

【问题讨论】:

  • 这意味着子类将比其父类看到更多。听起来确实有点奇怪。
  • @luk2302 包保护的方法不在包内也很奇怪。
  • 这是我设法找到的 JLS 中最相关的部分:docs.oracle.com/javase/specs/jls/se9/html/…
  • 假设您可以从类 A 中调用 method1,然后有人后来将一个名为 method1public 方法添加到类 M12 中。在那种情况下,应该从A 中调用哪个方法?如果调用了新方法,则添加新方法会改变行为。如果原来的method1 仍然被调用,你将拥有一个无法从特定包调用的公共方法。
  • 你可以调用method1,只是你必须转换为M1-((M1) this).method1()的类型-这对我来说很有意义,因为method1不是公共API的一部分M12.

标签: java packages


【解决方案1】:

可见性必须通过类层次结构。

类层次结构是A --> M12 --> M1

由于M1.method1M12 不可见,因此它的任何子类也不可见,例如A

【讨论】:

  • "可见性必须通过类层次结构。" - 为什么?
  • “为什么”——这是语言的工作方式——是否值得挖掘定义这一点的规范,或者只是知道“这就是语言的设计方式”?
  • 如果你方便的话,我会对规范中指向 this 的指针感兴趣。
  • “是否值得挖掘定义它的规范” - 我认为找到这个非常有趣。
  • 如果其他人发布了包含更多详细信息和参考的答案,我会赞成 - 我目前没有时间进行详细研究。我希望这足以帮助 OP 解决他们的问题并允许他们考虑替代设计来解决他们试图解决的问题
【解决方案2】:

我认为理解这种行为的最简单方法是“A 是 M12”的概念

当您声明继承时,您告诉 A 从 M12 获取其行为,但 M12 没有称为 method1 的可见方法。

让我们做一个有趣的实验:

public class M12 extends p1.M1 {
    public void method1() {
        System.out.println("Method 1 called");
    }
    void method2() {
        System.out.println("Method 2 called");
    }
}

忘记 A.. 当你声明这样的方法时,它是允许的 - 如果你没有 @Override 就可以了。 但是,如果 M1 是:

public class M1 {
    public void method1() {
        System.out.println("Method 1 called");
    }
}

你可以:

public class M12 extends p1.M1 {
    @Override
    public void method1() {
        System.out.println("Method 1 called");
    }

    void method2() {
        System.out.println("Method 2 called");
    }
}

现在,回到 M1 和 M2 的原始代码,重新声明方法并公开方法一:

public class M12 extends p1.M1 {
    public void method1() {
        System.out.println("Method 1 called");
    }

    public void method2() {
        System.out.println("Method 2 called");
    }
}

那么,你就可以拥有

public class A extends M12 {

    public void doSomething() {
        method1();
        method2();
    }
}

好的,这是一个微不足道的案例,但会丢失以完成序列... 最重要的是,从语义上讲,您可以通过 IS 关系来解释。


如果需要更多 (https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html):

下表显示了每个修饰符允许的成员访问权限。

访问级别

Modifier    Class   Package Subclass    World

public      Y       Y       Y           Y

protected   Y       Y       Y           N

no modifier Y       Y       N           N

private     Y       N       N           N

【讨论】:

  • 您可以方便地忽略A也是 M1。而M1 确实有一个方法method1p1 中可见。当然,我们可以公开所有内容并考虑这个 FTFY,但这不是问题所在。您引用了一个具有访问级别的表 - 有“没有修饰符 - 包 Y”。 AM1.method1() 在同一个包中,所以解释是什么?
  • 还要注意应该有帮助的过度使用注释行为。
  • 我没有忽略。情况似乎是一样的,它属于另一个答案,我赞成,因为有人反对它。这是一个奇怪的继承案例——是的 A 是 M1,但它不是它的行为来源,这是一种线性过程。我们可以在这里讨论子成员中的访问修饰符,因为它的行为相似。假设您的子类扩展了方法的访问级别。其子级的子级将无法恢复原始访问级别,只能进一步扩展它。像这样的奇怪情况在场景中会在深度时使继承变得糟糕。
  • 从语义上讲,让孙子继承父亲无法继承的东西真的很奇怪。就编程而言,如果语言允许在深层结构中进行此类行为,那将是非常混乱的。
  • 这种语言显然允许限制孩子做父亲可以做的事情。 M12不能用method1M1可以。
【解决方案3】:

首先,什么是类的成员? Java Language Specification 状态

类体可能包含类成员的声明,即 是,字段(§8.3),方法(§8.4),类(§8.5)和接口 (§8.5)。

它们是由什么组成的? JLS states

一个类类型的成员都有以下几种:

  • 成员从其直接超类(第 8.1.4 节)继承,但在没有直接超类的 Object 类中除外
  • 从任何直接超级接口继承的成员 (§8.1.5)
  • 成员在类主体中声明(第 8.1.6 节)

还提到了

只有声明为 protectedpublic 的类的成员才是 由包中声明的子类继承,而不是 声明了哪个类。

所有这些都在chapter on Inheritance中重新措辞

一个类C继承自它的直接超类所有具体方法m (静态和实例)的所有超类 以下是正确的:

  • mC 的直接超类的成员。
  • mpublicprotected在与 C` 相同的包中声明为具有包访问权限
  • C 中声明的任何方法的签名都不是m 签名的子签名(第 8.4.2 节)。

M1 类的成员是method1(以及Object 的所有方法)。 M12,与它的直接超类M1 位于不同的包中,不会继承method1。因此M12 的成员只有method2

B 的直接超类是M12,并且在同一个包中。因此它继承了它的成员method2Bmethod1 一无所知。如果您使用 javac 编译了代码,则会收到 cannot find symbol 编译错误。 (似乎 Eclipse 试图猜测您要做什么。)

同样,A 的直接超类是M12,但在不同的包中。由于这个原因,它不会继承method2Amethod1method2 一无所知,因为它没有继承它们。这两个都是找不到的符号。

【讨论】:

  • 请注意,其他答案确实说明了这一点,这就是为什么我从 Krease 对原件投赞成票的原因,而且,这是你如何为我的 @override 注释示例提供替代解释的帖子,如果继承过程和注释应该指向这篇文章的引用,则考虑到线性:如果您使用 javac 编译了代码,您将收到一个找不到符号编译错误。
猜你喜欢
  • 2014-10-09
  • 2013-11-25
  • 2018-02-11
  • 2018-05-27
  • 2012-03-05
  • 2023-04-08
  • 2016-06-23
  • 1970-01-01
相关资源
最近更新 更多