Type (class/interface) visibility can be managed at three levels:
1, at Java language level, a type can be declared as either public, private, or package private. A public type is visible globally, a private type (usually as a private nested type) is visible only to the enclosing element, and a package private type is visible only in the current package. This is the most common mechanism for handling type visibility and information hiding.
2, at module level, a type can be declared via module metadata to be exported externally, or kept strictly internal. Examples of such module systems are OSGi, Jigsaw, JBoss Modules, etc. In a runtime environment based on such module framework, the dependency and interaction between module components are clearly defined. Public classes in a module are not externally exposed unless declared so.
3, at component level, an implementation class can be proxied or wrapped to mitigate any external exposure. With 1 & 2, some types may still end up being exposed. But that is not too bad with a proxy or wrapper. Even though the client application can load the proxied implementation class, but what is directly exposed is the immutable proxy/wrapper.
For example, getServletContext() returns an implementation of javax.servlet.ServletContext, but it will not be the actual implementation class in the application server. Instead it is most likely an immutable proxy that exposes what is needed in javax.servlet.ServletContext interface. The similar pattern is also used in the implementation of ServletRequest, ServletResponse, javax.ejb.EJBContext, javax.ejb.TimerService, etc.
Why do we want to hide certain types? A software module provides services by publishing essential interfaces and classes, which become the liability of the module. These published types are expected to be there and maintained for the life of the module. When it comes time to redesign, you will need to evaluate how to keep backward compatibility while evolving the API to adapt to the new technology. This is also the time you really wish these interfaces/classes/methods had never been exposed.
Java EE application deployed to application server is an interesting case. User-provided application code and application server code cooperate to make the app work, but they should also be isolated from each other and keep a respectful distance. Application server internal implementation types should be completely hidden from user applications for security purpose. If the application packages the same library that the server contains, care must be taken that the server does not inadvertently load the application's version via thread context classloader.
Add a comment