Java › Java Platform Module System (JPMS)

How would you diagnose and resolve common JPMS migration errors, such as 'package is not visible' or a split package conflict, when modularizing an existing large application?

Advanced

Answer

'Package is not visible' errors typically indicate an attempt to access a package that exists in a required module but wasn't actually exported (or wasn't opened, for reflective access) by that module, resolved by either adding the appropriate exports/opens directive to the PROVIDING module (if you control it) or, if it's a third-party dependency you don't control, potentially needing to fall back to the classpath/unnamed-module approach for that specific dependency; split package conflicts (as covered earlier) occur when two modules on the module path both contain the same package name, resolved by keeping the conflicting JARs on the classpath instead, or by working with the affected library maintainers to properly separate the overlapping packages -- a broader, pragmatic migration strategy for large existing applications commonly involves an incremental approach: modularizing top-level application code first while leaving less-critical or harder-to-migrate dependencies on the classpath temporarily, gradually shrinking that classpath-based portion over time as dependencies get updated to properly support modules.
# Common error encountered during JPMS migration:
# java.lang.IllegalAccessError: class com.example.app.Main cannot access class
# com.example.lib.internal.Helper (in module com.example.lib) because module
# com.example.lib does not export com.example.lib.internal to module com.example.app

# Resolution: either the lib module adds 'exports com.example.lib.internal to com.example.app;'
# (a QUALIFIED export, limiting visibility to just this specific consumer),
# or the consuming code is refactored to avoid needing that internal package entirely
Real-world example A team modularizing a large legacy application encounters dozens of 'package is not visible' errors during their initial migration attempt, tracing most of them to internal utility packages that application code had been (perhaps unintentionally) relying on despite those packages never being part of the library's genuinely intended public API, using this migration process itself as a valuable forcing function to properly identify and clean up such unintended internal-API dependencies.

Common follow-ups: What's a qualified export ('exports package to specificModule'), and when would you use it instead of a plain unqualified export?;What incremental migration strategies help make a large-scale JPMS adoption more manageable than an all-at-once approach?