I have the follwing Java 9 module:
module com.example.a {
exports com.example.a;
}
With an exported type:
public class Api {
public static void foo(ImplDetail args) {}
}
And a non-exported type:
package com.example.b.internal;
public class ImplDetail {}
The exported type uses the non-exported type as a method parameter type in a public method. I'd have assumed that the compiler would reject such an inconsistent class configuration, as clients in other modules could not really invoke the foo()
method as they cannot instantiate the parameter type.
To my surprise, this module is compiled successfully by javac. I can see the special case of passing null
, still I'd consider such an API definition malformed and think it should not be supported, enforced by the compiler ideally.
What's the reasoning for not disallowing such case?
Certainly using a non-exported type in an API is bad style and is quite likely to be a design error, but it's fairly clear to me that javac isn't in a position to make this be a compile-time error.
Note that it has always been possible to use a private type in a public API, going all the way back to Java 1.0.
You already noted that code outside the module can still call
Api.foo(null)
.There are other cases where a caller could still use this API with a non-null reference. Consider a class
public class Sub extends ImplDetail
in packagecom.example.a
. This classSub
is public and is exported and so is available to code outside the module. Thus, outside code can callApi.foo(sub)
using instances ofSub
obtained from somewhere.But surely, javac can tell whether there are any subtypes of
ImplDetail
in any exported packages, and issue a compile-time error if there aren't any? Not necessarily. Because of the possibility of separate compilation, new classes might be introduced into a module after the compilation step that includesApi
. Or, for that matter, the module-info.class file could be recompiled to change the set of exported packages.For these reasons I think it's inappropriate for javac to raise an error at the time that it compiles the
Api
class. Javac does have an option-Xlint:exports
that will flag cases like this as a warning, however.Something later in the build process, such as the jmod tool, or some after-the-fact module auditing tool, could also flag the use of a non-exported type being used in an exported API. I don't think anything currently does this, though.