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

时间:2016-12-16 08:29:12

标签: java java-9 jigsaw

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

不解除此类案件的原因是什么?

1 个答案:

答案 0 :(得分:8)

当然在API中使用非导出类型是一种糟糕的风格,很可能是一个设计错误,但我很清楚javac是不是有能力做到这一点编译时错误。

请注意,始终可以在公共API中使用私有类型,一直回到Java 1.0。

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

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

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

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

在构建过程的后期,例如jmod工具或一些事后模块审计工具,还可以标记在导出的API中使用的非导出类型的使用。不过,我不认为现在有什么。