这在 Java 中是可能的:
package x;
public class X {
// How can this method be public??
public Y getY() {
return new Y();
}
}
class Y {}
那么 Java 编译器允许我将
getY()
方法声明为 public
的充分理由是什么?令我困扰的是:类 Y
是包私有的,但访问器 getY()
在其方法签名中声明了它。但在 x
包之外,我只能将方法的结果分配给 Object
:
// OK
Object o = new X().getY();
// Not OK:
Y y = new X().getY();
好的。现在我可以尝试编一个例子,用“方法结果协方差”来解释这一点。但更糟糕的是,我还可以这样做:
package x;
public class X {
public Y getY(Y result) {
return result;
}
}
class Y {}
现在我永远无法从
getY(Y result)
包之外调用
x
。为什么我可以这么做? 为什么编译器让我以无法调用的方式声明一个方法?
另一个包仍然可以使用 package-private 参数调用该方法。最简单的方法就是通过
null
。但这并不是因为您仍然可以调用它,这样的构造才真正有意义。
它打破了 package-private 背后的基本思想:只有包本身应该看到它。大多数人都会同意,任何使用此构造的代码至少都会令人困惑,而且味道不好。 最好不要允许。 顺便说一句,它被允许的事实带来了更多的极端情况。例如,从不同的包执行
Arrays.asList(new X().getY())
编译,但在执行时抛出 IllegalAccessError,因为它尝试创建不可访问的 Y 类的数组。这只是表明这种不可访问类型的泄漏
不符合语言设计其余部分所做的假设。 但是,就像 Java 中的其他“不寻常”规则一样,它在 Java 的第一个版本中是允许的。因为这不是什么大问题,而且因为向后兼容性
对于 Java 来说更重要,所以改善这种情况(不允许它)根本不再值得。 首先,你可以调用该方法。这个简单的例子是在同一个包中调用它。
public interface MyInterface { }
class HiddenImpl implements MyInterface { }
public class Factory {
public HiddenImpl createInstance() {
return new HiddenImpl();
}
}
当然,有人可能会争辩说,在这种情况下,编译器可以强制
createInstance()
的返回值为
MyInterface
。然而,允许它成为
HiddenImpl
至少有两个好处。一是,HiddenImpl
可以实现多个单独的接口,并且调用者可以自由选择要使用返回值的类型。另一种是来自包内部的调用者可以使用相同的方法来获取 HiddenImpl 的实例并按原样使用它,而无需对其进行强制转换或在工厂中拥有两个方法(一个公共 MyInterface createInstance()
和一个受包保护的方法) HiddenImpl createPrivateInstance()
)做同样的事情。允许类似
public void setY(Y param)
之类的原因是类似的。可能存在 Y
的公共子类型,来自包外部的调用者可以传递这些类型的实例。同样,与上面相同的两个优点也适用于此(可能有多个这样的子类型,来自同一包的调用者可以选择直接传递
Y
实例)。允许它的一个重要原因是允许不透明类型
是的,这很丑陋、愚蠢且毫无意义,但你仍然可以这样做。
也就是说,我相信你的原始代码会产生编译器警告。