为什么泄漏内部类型的公共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成功编译的。 我可以看到传递null
的特殊情况,但我仍然认为这样的API定义格式错误,并且认为它不应该被支持,理想情况下由编译器强制执行。
不解除这种情况的原因是什么?
当然在API中使用非导出类型是错误的样式并且很可能是设计错误,但我很清楚javac无法使其成为编译时错误。
请注意,始终可以在公共API中使用私有类型,一直返回到Java 1.0。
您已经注意到模块外部的代码仍然可以调用Api.foo(null)
。
在其他情况下,调用者仍然可以将此API与非null引用一起使用。 考虑一个类public class Sub extends ImplDetail
在包com.example.a
public class Sub extends ImplDetail
。 此类Sub
是公共的并且是导出的,因此可用于模块外部的代码。 因此,外部代码可以使用从某处获得的Sub
实例来调用Api.foo(sub)
。
但是肯定的是,javac可以判断任何导出的包中是否存在ImplDetail
任何子类型,如果没有,则发出编译时错误? 不必要。 由于可以进行单独编译,因此在包含Api
的编译步骤之后,可能会在模块中引入新类。 或者,就此而言,可以重新编译module-info.class文件以更改导出的包的集合。
由于这些原因,我认为javac在编译Api
类时引发错误是不合适的。 Javac确实有一个-Xlint:exports
选项-Xlint:exports
会将这样的情况标记为警告。
稍后在构建过程中的某些内容(例如jmod工具或一些事后模块审计工具)也可以标记在导出的API中使用的非导出类型。 不过,我认为目前没有任何事情可以做到这一点。