我有以下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定义格式错误,并认为它不应该得到支持,理想情况下由编译器强制执行。
不解除此类案件的原因是什么?
答案 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中使用的非导出类型的使用。不过,我不认为现在有什么。