如果在Java中声明一个私有内部类的成员为公共的,而它仍然无法在包含类之外被访问,那么这样做的原因是什么?或者可以吗?
public class DataStructure {
// ...
private class InnerEvenIterator {
// ...
public boolean hasNext() { // Why public?
// ...
}
}
}
如果在Java中声明一个私有内部类的成员为公共的,而它仍然无法在包含类之外被访问,那么这样做的原因是什么?或者可以吗?
public class DataStructure {
// ...
private class InnerEvenIterator {
// ...
public boolean hasNext() { // Why public?
// ...
}
}
}
interface EvenIterator {
public boolean hasNext();
}
public class DataStructure {
// ...
private class InnerEvenIterator implements EvenIterator{
// ...
public boolean hasNext() { // Why public?
// ...
}
}
InnerEvenIterator iterator;
public EvenIterator getIterator(){
return iterator;
}
}
为了表明这个方法在语义上是公共的,尽管编译器在这种情况下不强制执行可见性规则,因此可以将其设置为public
。
想象一下,在某些重构期间,您需要将此内部类转换为顶级类。如果此方法为private
,那么您如何决定是否应将其设置为public
或使用更严格的修饰符?将方法声明为public
可以告诉读者原始作者的意图 - 这个方法不应被视为实现细节。
当你实现任何 interface
时,它非常有用。
class DataStructure implements Iterable<DataStructure> {
@Override
public Iterator<DataStructure> iterator() {
return new InnerEvenIterator();
}
// ...
private class InnerEvenIterator implements Iterator<DataStructure> {
// ...
public boolean hasNext() { // Why public?
// ...
return false;
}
@Override
public DataStructure next() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
public static void main(String[] ex) {
DataStructure ds = new DataStructure();
Iterator<DataStructure> ids = ds.iterator();
ids.hasNext(); // accessable
}
}
我认为你在示例代码中漏掉了实现Iterator
接口的部分。在这种情况下,你不能使hasNext()
方法具有除public以外的任何其他可见性标识符,因为这将降低其可见性(接口方法具有public可见性),并且它将无法编译。
许多访问修饰符的组合是没有用处的。在私有内部类中定义公共方法只有在它实现了公共类/接口中的公共方法时才有用。
public class DataStructure {
// ...
private class InnerEvenIterator implements Iterator {
// ...
public boolean hasNext() { // Why public?
// ...
}
}
public Iterator iterator() {
return new InnerEvenIterator();
}
}
顺便说一下:抽象类通常具有public
构造函数,而实际上它们是protected
如果内部类是私有的,则无法在外部类之外通过名称访问它。 内部和外部类可以访问彼此的私有方法和私有实例变量。 只要在内部或外部类中,public和private修饰符具有相同的效果。 在您的代码示例中:
public class DataStructure {
// ...
private class InnerEvenIterator {
// ...
public boolean hasNext() { // Why public?
// ...
}
}
}
就数据结构类而言,这与以下内容完全等效:
public class DataStructure {
// ...
private class InnerEvenIterator {
// ...
private boolean hasNext() {
// ...
}
}
}
重写或隐藏方法的访问修饰符必须至少提供与被重写或隐藏方法相同的访问权限。
例如:
public class Outer {
private static class Nested implements Iterator<String> {
@Override
public boolean hasNext() {
...
}
@Override
public String next() {
...
}
}
}
hasNext()
和next()
重写了Iterator
方法,必须是public
,因为Iterator
方法是公共的。如果类型是可访问的,并且成员或构造函数被声明为允许访问,则只有引用类型的成员才能被访问。
[...]
否则,成员或构造函数被声明为
private
,并且仅当它出现在包含成员或构造函数声明的顶级类型(§7.6)的主体内时,访问才被允许。
因此,从源代码的角度来看,private
嵌套类的成员只能从封闭顶级类型的主体中访问。有趣的是,“主体”也包括其他嵌套类:
public class TopLevel {
private static class Nested1 {
private int i;
}
void doSomething(Nested1 n) {
// Can access private member of nested class
n.i++;
}
private static class Nested2 {
void doSomething(Nested1 n) {
// Can access private member of other nested class
n.i++;
}
}
}
从编译器提供的访问限制角度来看,在私有嵌套类中使用公共成员确实没有意义。
然而,使用不同的访问级别可以用于传达意图,特别是(正如其他人指出的那样),当嵌套类将来可能被重构为单独的顶层类时。考虑以下示例:
public class Cache {
private static class CacheEntry<T> {
private final T value;
private long lastAccessed;
// Signify that enclosing class may use this constructor
public CacheEntry(T value) {
this.value = value;
updateLastAccessed();
}
// Signify that enclosing class must NOT use this method
private void updateLastAccessed() {
lastAccessed = System.nanoTime();
}
// Signify that enclosing class may use this method
public T getValue() {
updateLastAccessed();
return value;
}
}
...
}
有趣的是注意到Java编译器如何处理对嵌套类成员的访问。在JEP 181: Nest-Based Access Control(Java 11中添加)之前,编译器必须创建合成访问器方法,因为类文件无法表达与嵌套类相关的访问控制逻辑。考虑以下示例:
class TopLevel {
private static class Nested {
private int i;
}
void doSomething(Nested n) {
n.i++;
}
}
javap -p ./TopLevel$Nested.class
进行检查时,您将看到已添加了一个合成的access$008
方法:class TopLevel$Nested {
private int i;
private TopLevel$Nested();
static int access$008(TopLevel$Nested);
}
javap -v
输出结果)。class TopLevel$Nested
...
{
private int i;
...
private TopLevel$Nested();
...
}
...
NestHost: class TopLevel
...
另一个有趣的方面是反射。JLS在这方面不太详细,但§15.12.4.3中包含了一个有趣的提示:
同样地,如果T与D不在同一包中,但它们的包在同一模块中,并且T是
public
或protected
,那么T是可访问的。[...]
如果T是
protected
,那么它必须是一个嵌套类型,因此在编译时,其可访问性受到包含其声明的类型的可访问性的影响。然而,在链接期间,它的可访问性不受包含其声明的类型的可访问性的影响。此外,在链接期间,protected
T与public
T一样可访问。
AccessibleObject.setAccessible(...)
并没有提及封闭类型。实际上,在非公共的封闭类型中可以访问公共或受保护的嵌套类型的成员:test1/TopLevel1.java
。package test1;
// package-private
class TopLevel1 {
private static class Nested1_1 {
protected static class Nested1_2 {
public static int i;
}
}
}
test2/TopLevel2.java
package test2;
import java.lang.reflect.Field;
public class TopLevel2 {
public static void main(String... args) throws Exception {
Class<?> nested1_2 = Class.forName("test1.TopLevel1$Nested1_1$Nested1_2");
Field f = nested1_2.getDeclaredField("i");
f.set(null, 1);
}
}
test1.TopLevel1.Nested1_1.Nested1_2.i
,而无需让其可访问,尽管它位于一个包私有类内部的私有嵌套类中。private
或包私有。