我有以下
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.考虑一个类public class Sub在包com.example.a中扩展ImplDetail.这个类Sub是public的,并且被导出,因此可以在模块之外进行编码.因此,外部代码可以使用从某处获取的Sub的实例调用Api.foo(sub).
但是肯定的是,javac可以告诉任何导出的包中是否存在ImplDetail的子类,如果没有,则会发出编译时错误?不必要.由于单独编译的可能性,在包含Api的编译步骤之后,可能会将新类引入到模块中.或者,为此,可以重新编译module-info.class文件以更改导出的包的集合.
由于这些原因,我认为javac在编译Api类时引发错误是不合适的.然而,Javac确实有一个选项-Xlint:exports,它会将这种情况标记为警告.
在构建过程中,如jmod工具或某些事后模块审核工具之后,还可以标记在导出的API中使用的非导出类型.但是,我不认为目前有什么.