java - 通过公共(public) API 导出非公共(public)类型

标签 java api inheritance

如果我只有很少的返回非公共(public)类型的工厂方法和一对给出这种非公共(public)类型变量的方法怎么办?这会在 NetBeans 中产生带标题的警告消息。

结果,公共(public) API 将仅包含两组配对方法。原因是使我的类型层次结构密封(就像 Scala 中的密封类)并允许用户仅通过工厂方法实例化这些类型。所以我们在某种意义上得到了 DSL。

例如,日程表类由日历字段的约束表示。有一些类型的约束——Range、Singleton、List、FullSet——以 NumberSet 接口(interface)为根。我们不想公开这些类型以及 Schedule 如何与它们交互。我们只需要用户的规范。所以我们让 NumberSet 包私有(private)。在 Schedule 类中,我们为约束创建了几个工厂方法:

NumberSet singleton(int value);
NumberSet range(int form, int to);
NumberSet list(NumberSet ... components);

以及一些创建Schedule对象的方法:

Schedule everyHour(NumberSet minutes);
Schedule everyDay(NumberSet minutes, NumberSet hours);

用户只能按照以下方式使用它们:

Schedule s = Schedule.everyDay( singleton(0), list(range(10-15), singleton(8)) );

这是个坏主意吗?

最佳答案

这个想法本身是合理的。但如果您将根类型(此处:NumberSet)包设为私有(private),它就不会起作用。公开该类型或改用公共(public)接口(interface)。应该(并且可以)隐藏实际的实现类型。

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
}

编辑 从公共(public) API 公开隐藏/私有(private)根类型是错误的。考虑以下场景:

package example;

class Bar {
    public void doSomething() {
            // ...
    }
}

public class Foo {

    public Bar newBar() {
        return new Bar();
    }
}

并考虑使用此 API 的客户端应用程序。客户能做什么?它不能正确地将变量声明为 Bar 类型,因为该类型对于 example 包之外的任何类都是不可见的。它甚至不能在从某处获得的 Bar 实例上调用 public 方法,因为它不知道这样一个公共(public)方法的存在(它看不到类,更不用说它公开的任何成员)。因此,客户在这里可以做的最好的事情是:

Object bar = foo.newBar();

这基本上是无用的。另一件事是拥有一个公共(public)接口(interface)(或抽象类)而不是包私有(private)接口(interface),如上面定义的代码。在这种情况下,客户端实际上可以声明NumberSet 类型的变量。它不能创建自己的实例或派生子类,因为构造函数对它是隐藏的,但它可以访问定义的公共(public) API。

再次编辑 即使您想要一个“无特征”的值(从客户端的角度来看),即一个没有定义客户端可能想要调用的任何有趣 API 的值,它仍然是以上述方式公开公共(public)基类型是个好主意。并且仅出于编译器能够执行类型检查并允许客户端代码将此类值临时存储到(正确声明的)变量中的目的。

如果您不希望您的客户端调用该类型的任何 API 方法:那很好。没有什么可以阻止您不为您的(否则)公共(public)基类型提供公共(public) API。只是不要声明任何东西。使用“空”抽象基类(从客户端的角度来看是空的,因为所有有趣的方法都将被包私有(private)并因此被隐藏)。但是您仍然必须提供一个公共(public)基类型,或者您应该使用普通的 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
    }
}

就客户端代码而言,上述类是“空的”;除了 Object 已经提供的内容外,它不提供可用的 API。拥有它的主要好处是:客户端可以声明这种类型的变量,并且编译器能够进行类型检查。但是,您的框架仍然是唯一可以创建此类具体实例的地方,因此,您的框架可以完全控制所有值。

关于java - 通过公共(public) API 导出非公共(public)类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2744851/

相关文章:

api - BlueSnap 提供支付 API 和扩展支付 API 集成,它们之间有什么区别?

java - Java中如何使用Child对象调用Parent类的方法

java - Apache POI 修改图表而不使用命名范围

java - Hibernate 中多个模式中的相同表

api - 体面的视频聊天 API?

python-3.x - 如何在python中修改/覆盖继承的类函数?

c++如何使用子类调用函数,具有父类(super class)指针

java - 检测扫描仪是否没有下一个

java - Hashmap put(),是不是一直在排序?

api - 为什么YouTube供稿上的数据与真实数据不同?