为啥 Java 中没有子类可见性修饰符?

Posted

技术标签:

【中文标题】为啥 Java 中没有子类可见性修饰符?【英文标题】:Why is there no sub-class visibility modifier in Java?为什么 Java 中没有子类可见性修饰符? 【发布时间】:2011-07-15 02:00:39 【问题描述】:

在不止一次的情况下,我发现自己想要一种在 Java 中无法实现的变量可见性。我希望某些成员在他们自己的类和任何子类中可见,但对包的其他部分或世界其他部分不可见。换句话说,我想要这个:

Modifier        Class     Package   Subclass  World
sub-class       Y         N         Y         N

然而Java的设计者只给了我this:

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

我想要这样的典型情况是创建抽象类时。有时我发现抽象的父级需要访问某些成员,但具体的子级也需要。我可以通过将成员设为protected 来授予他们此访问权限,但是当我真的不想这样做时,这会打开对软件包其余部分的访问权限。

完全清楚,我知道这样的修饰符在 Java 中是不可能的。我的问题是为什么 Java 中不包含这样的修饰符?它似乎(对我来说)比protected 或默认值更自然。仅仅是因为它的原因不够重要而不能被包括在内,还是与我没有考虑到的可能的副作用更相关?

【问题讨论】:

作为这种限制的解决方法,您可以将变量设为私有,然后使用静态内部类来实现。 也许是因为通常人们创建的子类与父类位于不同的包中?一个典型的例子是扩展第三方库来提供我们自己的实现。 @adarshr:这就是子类修饰符存在的确切原因。如果所有子类都与超类在同一个包中,则它与受保护的没有任何不同。 碰巧Java 1.0 有private protected。我相信实施是错误的。在 1.1 中删除。 我也很怀念这种可见性,原因与@Michael 相同。将我的类放在一个单独的包(抽象超类和具体子类)中只是为了限制可见性,感觉有点“不必要”。还制作了这样的hacky解决方案,例如使超类具体化,将其作为私有成员变量添加到子类中,实现一些接口等等,它使代码比它应该的更复杂。通过在继承的同时进行封装,私有保护确实可以使事情变得更好,从而将复杂性降到最低。 【参考方案1】:

我想他们希望通过非线性访问层次结构来避免增加的复杂性。

你应该可以控制你的包,所以不要在那里调用这些受保护的方法。

(顺便说一下,protectedsub-class and package 不太一样,因为不能在声明类的任意对象上调用非静态受保护方法(如果不在同一个包中),但只能在代码所在子类的对象上。(您可以在Object.clone() 上看到这一点,它只能由其对象被克隆的类调用。)

【讨论】:

我不明白括号中的评论。 protected is 子类可见性 + 包可见性。什么意思? @ewernli protected 有点少。尽管每个类都继承自 Object(即 clone() 应该在任何地方都可见),但您不能为任意对象调用 anyObject.clone() 以类似的方式,类的任何实例都可以访问同一类的另一个实例的private 成员。你会认为因为成员是private,所以只有那个实例应该能够访问它,但正如@PaŭloEbermann 所说,你已经完全控制了这个类。如果您正在访问另一个实例的private 成员,那是因为 是这样设计的。同样,如果您正在开发一个包并访问另一个类的 protected 成员,那是因为 是开发该包的人,而其他人无权访问。跨度> 旧 cmets/问题要发表评论,但有人可以澄清一下声明 - “你应该控制你的包裹”吗?如果我有一个 3rd 方库,那么我总是可以创建一个具有相同层次结构的包,并在需要时访问受保护的成员。不是这样吗? @Mubin 就是这种情况,是的(如果没有限制哪个类加载器可以在包中创建类的安全管理器)。但是您也可以创建第三方库的修改版本,并公开私人成员。或者使用反射。所有这些访问修饰符根本不防水。【参考方案2】:

Being-in-the-same-package 被简单地视为比 being-a-subtype-of 更密切的关系。

为什么?

您通常控制您正在开发的软件包的所有源代码(*),因此您至少有可能性来避免进行错误调用。

您确实控制所有扩展您的类的代码。 (任何人都可以扩展你的类。)这意味着包私有访问扮演着更重要的角色。


*) 但是,嘿,我用package com.yourpackage; 开始任何源文件,这样你就不能控制包中的所有代码!嗯,是的,但是 a) 你真的不应该这样做,b) sealing the packages 可以防止它。

【讨论】:

我无法决定我认为哪种关系更接近。我的直觉是 being-a-subtype-of 是一种更密切的关系(也许这就是我认为这种类型的修饰符比受保护和默认更有意义的部分原因)。但是,一些使用我创建的 JAR 库的随机编码器可以子类化我的一个类,而我可能知道我的包中发生了什么。嗯... 是的。也正是我的想法。如果您seal the package,您甚至可以在最后一句话中打出“可能” 我很难想象一个您不想要密封包裹的场景。【参考方案3】:

您应该将您的类放在它自己的包中,并将成员(实例变量或方法)标记为受保护。 这样,除了子类之外,没有其他类可以访问您推销为受保护的成员。 如果您迫切希望仅子类访问该受保护成员,那么您最终将在一个包中得到一个类。

【讨论】:

以上是关于为啥 Java 中没有子类可见性修饰符?的主要内容,如果未能解决你的问题,请参考以下文章

对比Java学Kotlin可见性修饰符

对比Java学Kotlin可见性修饰符

在java中,如果没有给变量指定是公有或是私有,默认是啥?

Java中带有抽象方法签名的可见性修饰符

Kotlin基础-可见修饰符嵌套类

public private protected 三种访问修饰符在c#中的区别