为什么泄漏内部类型的公共 API 编译不会失败?

我有一个java 9模块:

module com.example.a {
    exports com.example.a;
}

使用导出的类型:

public class Api {

    public static void foo(ImplDetail args) {}
}

以及非导出类型:

package com.example.b.internal;

public class ImplDetail {}

导出的类型使用非导出类型作为公共方法中的方法参数类型。我假设编译器会拒绝这种不一致的类配置,因为其他模块中的客户端无法真正调用该方法,因为它们无法实例化参数类型。foo()

令我惊讶的是,这个模块是由javac成功编译的。我可以看到传递的特殊情况,我仍然认为这样的API定义格式不正确,并认为它不应该被支持,理想情况下由编译器强制执行。null

不允许这种情况的理由是什么?


答案 1

当然,在API中使用非导出类型是不好的风格,很可能是设计错误,但对我来说很清楚,javac无法使这成为编译时错误。

请注意,在公共API中使用私有类型一直都是可能的,一直追溯到Java 1.0。

您已经注意到模块外部的代码仍然可以调用 。Api.foo(null)

在其他情况下,调用方仍可将此 API 与非空引用一起使用。考虑包 中的类。此类是公共的,并且已导出,因此可用于模块外部的代码。因此,外部代码可以使用从某处获取的 实例进行调用。public class Sub extends ImplDetailcom.example.aSubApi.foo(sub)Sub

但是可以肯定的是,javac可以判断任何导出的包中是否存在任何子类型,如果没有任何子类型,则发出编译时错误?不一定。由于可以单独编译,因此在编译步骤之后,可能会在包含 的模块中引入新类。或者,就此而言,可以重新编译模块信息.class文件以更改导出的包集。ImplDetailApi

由于这些原因,我认为javac在编译类时引发错误是不合适的。但是,Javac确实有一个选项可以将此类情况标记为警告。Api-Xlint:exports

构建过程后期的某些内容(如 jmod 工具或一些事后模块审核工具)也可以标记在导出的 API 中使用的非导出类型的使用。不过,我不认为目前有任何东西可以做到这一点。


答案 2

推荐