这个想法本身是合理的。但如果你这样做的话,这是行不通的根型 (here: NumberSet
) 包私有。公开该类型或使用公共接口。实际的实现类型应该(并且可以)隐藏。
public abstract class NumberSet {
// Constructor is package private, so no new classes can be derived from
// this guy outside of its package.
NumberSet() {
}
}
public class Factories {
public NumberSet range(int start, int length) {
return new RangeNumberSet(start, length);
}
// ...
}
class RangeNumberSet extends NumberSet {
// ... must be defined in the same package as NumberSet
// Is "invisible" to client code
}
Edit从公共 API 公开隐藏/私有根类型是一个错误。考虑以下场景:
package example;
class Bar {
public void doSomething() {
// ...
}
}
public class Foo {
public Bar newBar() {
return new Bar();
}
}
并考虑使用此 API 的客户端应用程序。客户可以做什么?它无法正确声明变量具有类型Bar
因为该类型对于包外的任何类都是不可见的example
。它甚至不能调用public
上的方法Bar
它从某个地方获得实例,因为它不知道存在这样的公共方法(它看不到该类,更不用说它公开的任何成员了)。因此,客户在这里可以做的最好的事情是:
Object bar = foo.newBar();
这本质上是没有用的。另一件事是使用公共接口(或抽象类)而不是包私有接口,如上面定义的代码所示。在这种情况下,客户端实际上can声明一个变量类型NumberSet
。它无法创建自己的实例或派生子类,因为构造函数对其隐藏,但它可以访问定义的公共 API。
再次编辑即使您想要一个“无特征”值(从客户端的角度来看),即一个没有定义客户端可能想要调用的任何有趣 API 的值,以这种方式公开公共基类型仍然是一个好主意如上所述。仅仅是为了编译器能够执行类型检查并允许客户端代码将这样的值临时存储到(正确声明的)变量中。
如果您不希望客户端调用该类型的任何 API 方法:那也可以。没有什么可以阻止您不在您的(否则)公共基类型上提供公共 API。只是不要声明任何。使用“空”抽象基类(从客户端的角度来看是空的,因为所有有趣的方法都是包私有的,因此是隐藏的)。但你仍然必须提供一个公共基础类型,或者你应该使用简单的Object
作为返回值,但是您会在编译时放弃错误检查。
经验法则:如果客户端必须调用某个方法才能获取值并将其传递给其他 API,那么客户端实际上knows,有一些神奇的特殊值。它必须能够以某种方式处理它(至少“传递它”)。除了(完全适当的)编译器警告之外,不为客户端提供正确的类型来处理这些值不会给您带来任何好处。
public abstract class Specification {
Specification() {
// Package private, thus not accessible to the client
// No subclassing possible
}
Stuff getInternalValue1() {
// Package private, thus not accessible to the client
// Client cannot call this
}
}
就客户端代码而言,上面的类是“空”的;除了一些东西之外,它不提供可用的 API,Object
已经优惠了。拥有它的主要好处是:客户端可以声明这种类型的变量,并且编译器能够进行类型检查。不过,您的框架仍然是唯一可以创建这种类型的具体实例的地方,因此,您的框架可以完全控制所有值。