13

我有以下 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 编译成功。我可以看到 pass 的特殊情况null,但我仍然认为这样的 API 定义格式错误,并认为它不应该被支持,理想情况下由编译器强制执行。

不允许这种情况的原因是什么?

4

1 回答 1

8

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

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

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

在其他情况下,调用者仍可以将此 API 与非空引用一起使用。考虑public class Sub extends ImplDetailpackage中的一个类com.example.a。这个类Sub是公共的并且是导出的,因此可用于模块外的代码。因此,外部代码可以使用从某处获得的Api.foo(sub)实例来调用。Sub

但可以肯定的是,javac 可以判断任何导出的包中是否有任何子类型,ImplDetail如果没有,则发出编译时错误?不必要。由于单独编译的可能性,新类可能会在包含Api. 或者,就此而言,可以重新编译 module-info.class 文件以更改导出的包集。

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

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

于 2016-12-16T23:16:24.237 回答