为什么在Java中将私有内部类成员公开?
如果在 Java 中将私有内部类的成员声明为 public,但仍然无法在包含类之外访问该成员,那么原因是什么?或者可以吗?
public class DataStructure {
// ...
private class InnerEvenIterator {
// ...
public boolean hasNext() { // Why public?
// ...
}
}
}
What is the reason of declaring a member of a private inner class public in Java if it still can't be accessed outside of containing class? Or can it?
public class DataStructure {
// ...
private class InnerEvenIterator {
// ...
public boolean hasNext() { // Why public?
// ...
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
有许多没有用的访问修饰符组合。私有内部类中的公共方法仅在公共类/接口中实现公共方法时才有用。
顺便说一句:抽象类通常具有
public
构造函数,而实际上它们是protected
There are many combinations of access modifiers which are not useful. A public method in a private inner class is only useful if it implements a public method in a public class/interface.
BTW: abstract classes often have
public
constructors when actually they areprotected
如果内部类是私有的,则不能通过外部类外部的名称来访问它。内部类和外部类可以访问彼此的私有方法和私有实例变量。只要在内部类或外部类中,修饰符 public 和 private 就具有相同的效果。在您的代码示例中:
就 DataStructure 类而言,这完全等同于:
这是因为只有 DataStructure 可以访问它,因此将其设置为 public 还是 private 并不重要。不管怎样,DataStructure 仍然是唯一可以访问它的类。使用您喜欢的任何修饰符,功能上没有区别。唯一不能随机选择的时间是在实施或扩展时,在这种情况下您不能减少访问权限,但可以增加访问权限。因此,如果抽象方法具有受保护的访问权限,您可以将其更改为公共。当然,两者实际上都没有任何区别。
如果您计划在其他类中使用内部类,并因此将其公开,那么您可能不应该首先将其设为内部类。
此外,我没有看到内部类扩展或实现其他类的任何要求。他们这样做可能很常见,但这肯定不是必需的。
If the inner class is private it cannot be accessed by name outside of the outer class. Inner and outer classes have access to each other's private methods and private instance variables. As long as you are within the inner or outer class, the modifiers public and private have the same effect. In your code example:
As far as the class DataStructure is concerned, this is completely equivalent to:
This is because only DataStructure can access it, so it doesn't matter if you set it to public or private. Either way, DataStructure is still the only class that can access it. Use whichever modifier you like, it makes no functional difference. The only time you can't choose at random is when you are implementing or extending, in which case you can't reduce the access, but you can increase it. So if an abstract method has protected access you can change it to public. Granted neither one actually makes any difference.
If you plan on using an inner class in other classes, and therefore making it public, you probably shouldn't make it an inner class in the first place.
Additionally, I don't see any requirement for inner classes extending or implementing other classes. It might be common for them to do so, but it's certainly not required.
这里必须考虑多个方面。下面将使用术语“嵌套类”,因为它涵盖非
静态
(也称为“内部类”)和静态
类(源)。与
private
嵌套类无关,但 JLS §8.2 有一个有趣的 示例 显示了包私有或protected
类中的public
成员可能有用的地方。源代码
重写方法
当嵌套类实现接口或扩展类并重写其方法之一时,则按照 JLS §8.4.8.3:
例如:
重写的方法
hasNext()
和next()
Iterator
方法必须是public
,因为Iterator
方法是公共的。作为旁注: JLS §13.4.7 描述了类可以提高其方法之一的访问级别,即使子类覆盖它,也不会导致链接错误。
传达意图
访问限制在 JLS §6.6.1:
因此,私有嵌套类的成员(从源代码的角度来看;另请参见“反射”部分)只能从封闭的顶级类型的主体中访问。有趣的是,“主体”还涵盖了其他嵌套类:
因此,从编译器提供的访问限制的角度来看,在
private
嵌套类中使用public
成员确实没有意义。然而,使用不同的访问级别对于传达意图非常有用,特别是(正如其他人指出的那样)当嵌套类将来可能被重构为单独的顶级类时。考虑这个例子:
编译的类文件
注意 Java 编译器如何处理对嵌套类成员的访问也很有趣。在 JEP 181:基于 Nest 的访问控制(在 Java 11 中添加)之前,编译器必须创建合成访问器方法,因为类文件无法表达与嵌套类相关的访问控制逻辑。考虑这个例子:
-p ./TopLevel$Nested.class 检查时,您将看到添加了一个合成
access$008
方法:当使用 Java 8 编译并使用
javap 稍微增加了类文件的大小,并且可能会降低性能。这就是为什么经常为嵌套类的成员选择包私有(即无访问修饰符)访问以防止创建合成访问方法的原因之一。
对于 JEP 181,这不再是必要的(使用 JDK 11 编译时的 javap -v 输出):
反射
另一个有趣的方面是反射。遗憾的是,JLS 在这方面没有具体验证,但 §15.12.4.3 包含一个有趣的提示:
类似地,
AccessibleObject.setAccessible(...)
< /a> 没有提到完全封闭的类型。事实上,可以访问非public
封闭类型中的public
或protected
嵌套类型的成员:test1/TopLevel1.java
test2/TopLevel2.java
这里反射能够修改字段
test1.TopLevel1.Nested1_1.Nested1_2.i
而无需使其可访问,尽管它位于包私有类内的private
嵌套类中。当您为运行不受信任的代码的环境编写代码时,您应该记住这一点,以防止恶意代码干扰内部类。
因此,当涉及嵌套类型的访问级别时,您应该始终选择最不宽松的类型,最好是
私有
或包私有。There are multiple aspects which have to be considered here. The following will use the term "nested class" because it covers both non-
static
(also called "inner class") andstatic
classes (source).Not related to
private
nested classes, but JLS §8.2 has an interesting example which shows wherepublic
members in package-private orprotected
classes could be useful.Source code
Overriding methods
When your nested class implements an interface or extends a class and overrides one of its methods, then per JLS §8.4.8.3:
For example:
The methods
hasNext()
andnext()
which override theIterator
methods have to bepublic
because theIterator
methods are public.As a side note: JLS §13.4.7 describes that it is possible for a class to increase the access level of one of its methods, even if a subclass overrides it with, without causing linkage errors.
Conveying intention
Access restriction is defined in JLS §6.6.1:
Therefore members of a
private
nested class can (from a source code perspective; see also "Reflection" section) only be accessed from the body of the enclosing top level type. Interestingly the "body" also covers other nested classes:So from a compiler-provided access restriction perspective there is indeed no point in using a
public
member in aprivate
nested class.However, using different access levels can be useful for conveying intention, especially (as pointed out by others) when the nested class might be refactored to a separate top level class in the future. Consider this example:
Compiled class files
It is also interesting to note how the Java compiler treats access to members of nested classes. Prior to JEP 181: Nest-Based Access Control (added in Java 11) the compiler had to create synthetic accessor methods because the class file could not express the access control logic related to nested classes. Consider this example:
When compiled with Java 8 and inspected with
javap -p ./TopLevel$Nested.class
you will see that a syntheticaccess$008
method has been added:This slightly increased the size of the class files and might have decreased performance. This is one reason why package-private (i.e. no access modifier) access has often be chosen for members of nested classes to prevent creation of synthetic access methods.
With JEP 181 this is no longer necessary (
javap -v
output when compiled with JDK 11):Reflection
Another interesting aspect is reflection. The JLS is sadly not verify specific in that regard, but §15.12.4.3 contains an interesting hint:
Similarly
AccessibleObject.setAccessible(...)
does not mention the enclosing type at all. And indeed it is possible to access the members of apublic
orprotected
nested type within non-public
enclosing type:test1/TopLevel1.java
test2/TopLevel2.java
Here reflection is able to modify the field
test1.TopLevel1.Nested1_1.Nested1_2.i
without having to make it accessible despite it being inside aprivate
nested class inside a package-private class.When you are writing code for an environment where untrusted code is run you should keep that in mind to prevent malicious code from messing with internal classes.
So when it comes to the access level of nested types you should always choose the least permissive one, ideally
private
or package-private.如果 InnerEvenIterator 类没有扩展任何类或实现任何接口,我认为这是无稽之谈,因为没有其他类可以访问它的任何实例。
但是,如果它扩展或实现任何其他非私有类或接口,那就有意义了。一个例子:
If the
InnerEvenIterator
class does not extend any class or implement any interface, I think it is nonsense because no other class can access any instance of it.However, if it extends or implements any other non private class or interface, it makes sense. An example:
该方法可以设为
public
,以表明它在语义上是公开的,尽管编译器在这种特殊情况下并不强制执行可见性规则。想象一下,在一些重构过程中,您需要使这个内部类成为顶级。如果此方法是
私有
,您如何决定是否应将其设为公共
,或者应使用一些更具限制性的修饰符?将方法声明为public
告诉读者原作者的意图 - 该方法不应被视为实现细节。This method can be made
public
in order to indicate that it's semantically public, despite the fact that compiler doesn't enforce visibility rules in this particular case.Imagine that during some refactoring you need to make this inner class top-level. If this method is
private
, how would you decide whether it should be madepublic
, or some more restrictive modifier should be used? Declaring method aspublic
tells reader the intentions of original author - this method shouldn't be considered an implementation detail.当您实现任何
接口
时它非常有用。It is useful when you implement any
interface
.我认为您缺少示例代码中实现
Iterator
接口部分。在这种情况下,您不能使hasNext()
方法具有除 public 之外的任何其他可见性标识符,因为这最终会降低其可见性(接口方法具有公共可见性) )并且它不会编译。I think you are missing the implementing the
Iterator
interface part in your sample code. In that case, you can't make thehasNext()
method have any other visibility identifier other than public since that would end up reducing its visibility (interface methods have public visibility) and it won't compile.