在方法签名中公开声明包私有类型

问题描述 投票:0回答:5

这在 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
。为什么我可以这么做?
为什么编译器让我以无法调用的方式声明一个方法?

java syntax package-private
5个回答
6
投票

另一个包仍然可以使用 package-private 参数调用该方法。最简单的方法就是通过

null

。但这并不是因为您仍然可以调用它,这样的构造才真正有意义。

它打破了 package-private 背后的基本思想
:只有包本身应该看到它。大多数人都会同意,任何使用此构造的代码至少都会令人困惑,而且味道不好。 最好不要允许。 顺便说一句,它被允许的事实带来了更多的极端情况。例如,从不同的包执行

Arrays.asList(new X().getY())

编译,但在执行时抛出 IllegalAccessError,因为它尝试创建不可访问的 Y 类的数组。这只是表明这种不可访问类型的泄漏

不符合语言设计其余部分所做的假设
但是,就像 Java 中的其他“不寻常”规则一样,它在 Java 的第一个版本中是允许的。因为这不是什么大问题,而且因为

向后兼容性

对于 Java 来说更重要,所以改善这种情况(不允许它)根本不再值得。 首先,你可以调用该方法。这个简单的例子是在同一个包中调用它。


2
投票

package x; public class Z extends Y {} package x2; x.getY( new Z() ); // compiles

但这不是重点。

重点是,Java 试图禁止一些明显无意义的设计,但它不能禁止全部。 

什么是无稽之谈?这是非常主观的。

  1. 如果太严格,在事物还是可塑性的时候就不利于开发。

  2. 语言规范已经太复杂了;添加更多规则超出了人类的能力。 [1]

  3. [1]

    http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.6

有时使用返回非

public

2
投票

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
实例)。
    
允许它的一个重要原因是允许

不透明类型

1
投票

package x; public interface Foo; class X { public Foo getFoo ( ) { return new Y( ); } class Y implements Foo { } } 这里我们有您的情况(通过公共 API 导出的受包保护的内部类),但这是有道理的,因为就调用者而言,返回的 Y 是不透明类型。也就是说,IIRC NetBeans 确实对此类行为发出了警告。


你不能做这样的事情吗:


1
投票

是的,这很丑陋、愚蠢且毫无意义,但你仍然可以这样做。

也就是说,我相信你的原始代码会产生编译器警告。

© www.soinside.com 2019 - 2024. All rights reserved.