这个想法本身是合理的。但是,如果您将 root 类型(此处为: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
}
编辑 从公共 API 公开隐藏/私有根类型是错误的。考虑以下场景:
package example;
class Bar {
public void doSomething() {
// ...
}
}
public class Foo {
public Bar newBar() {
return new Bar();
}
}
并考虑使用此 API 的客户端应用程序。客户能做什么?它不能正确地将变量声明为类型为Bar,因为该类型对于包example 之外的任何类都是不可见的。它甚至不能在它从某个地方获得的Bar 实例上调用public 方法,因为它不知道存在这样的公共方法(它看不到该类,更不用说它公开的任何成员)。因此,客户可以在这里做的最好的事情是:
Object bar = foo.newBar();
这基本上是无用的。另一件事是有一个公共接口(或抽象类)而不是包私有接口,如上面定义的代码。在这种情况下,客户端实际上可以声明一个NumberSet 类型的变量。它无法创建自己的实例或派生子类,因为构造函数对其隐藏,但它可以访问定义的公共 API。
再次编辑即使你想要一个“无特征”的值(从客户端的角度来看),即一个没有定义客户端可能想要调用的任何有趣 API 的值,它仍然是以上述方式公开公共基类型是一个好主意。并且只是为了编译器能够执行类型检查并允许客户端代码将此类值临时存储到(正确声明的)变量中。
如果您不希望您的客户端调用该类型的任何 API 方法:那很好。没有什么可以阻止您不在(否则)公共基础类型上提供公共 API。只是不要声明任何东西。使用“空”抽象基类(从客户端的角度来看是空的,因为所有有趣的方法都是包私有的,因此是隐藏的)。但是你仍然必须提供一个公共的基类型,或者你应该使用普通的Object 作为返回值,但是你会放弃编译时的错误检查。
经验法则:如果客户端必须调用某个方法来获取值并将其传递给其他 API,那么客户端实际上知道,存在一些神奇的特殊值.它必须能够以某种方式处理它(至少“传递它”)。除了(完全合适的)编译器警告之外,不为客户端提供适当的类型来处理这些值不会给您带来任何好处。
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 已经提供的东西。拥有它的主要好处:客户端可以声明这种类型的变量,并且编译器能够进行类型检查。但是,您的框架仍然是唯一可以创建这种类型的具体实例的地方,因此,您的框架可以完全控制所有值。