Java : How do I prevent my non public classes from being used outside my lib

There is a simple problem that a lot of developers may ask to their teammate or themself :
– I wrote a MyComponent class in a lib project, under the bar package

package bar;
public class MyComponent {
public void doIt () {}
}

– This class is not going to be used outside this project
– Then, how can I hide this to the users ? How can I prevent the users to play with the class MyComponent ? How can they consider my lib as a trustable and reliable black box instead of a collection of mysterious functions ?

Maybe there is another way… I will explain mine, and I think there are other ways to do that, better or worse.

1. Let’s try to declare a contract between you and the client code
A smarter approach can be to implement an interface with all the public methods which can be called from outside.

package bar;
public class MyComponent implements Component {
public void doIt () {}
}

public interface Component {
void doIt ();
}

This is a nice try, because now the client code will be advised to use Component as the preferred type to execute your methods.
You will still need to call the MyComponent constructor this way :

Component fooComponent = new MyComponent ();

The client code does not need to know what kind of Component we have.

2. Isolate the construction of your MyComponent class
You can think of a static method to reach the MyComponent class constructor.
Remove the public modifier in the MyComponent class.
Make the MyComponent constructor package scope so it is not usable by some reflection libs unless cracked in a dirty way.
Make the MyComponent class final so nobody can cheat with it.

package bar;
final class MyComponent implements Component {
MyComponent () {}
public void doIt () {}
}

Create an accessor in the same package as MyComponent, and leave it abstract :

package bar;
public abstract class ComponentAccessor {
public Component provideComponent (){
return new MyComponent ();
}
}

3. Separate the interface and the impl
If your project has different levels of abstractions, it can be useful to separate MyComponent and Component in different Java projects.
In my case, for my own lib, I only had to place MyComponent and Component in different package names, let’s say bar.model for Component and bar.infra.service for MyComponent and ComponentAccessor.

4. Build an accessor tree hierarchy
Imagine you have several MyComponent classes and several Component interfaces in different packages.
You can build an Accessor in each package. But how can you summarize them in one class ? Here is the tip : just make them extend each other !

package bar.infra.service;
import bar.model.MyComponent;
public abstract class ComponentAccessor {
public Component provideComponent (){
return new MyComponent ();
}
}

package bar.infra.service.operation;
import bar.model.operation.MyOperation;
public abstract class OperationAccessor extends ComponentAccessor {
public Operator provideTheOperator (){
return new MyOperation ();
}
}

package bar.infra.service.reader;
import bar.model.operation.MyReader;
public abstract class ReaderAccessor extends OperationAccessor {
public Reader provideTheReader (){
return new MyReader ();
}
}

Create a final class for your final accessor. Depending on your needs, you can leave it public. This will not break the principle.

package bar.ioc;
import bar.infra.service.reader;
public final class FinalAccessor extends ReaderAccessor {
}

Now, you can load your final accessor as if it was an IoC module and load your dependency in a clean and human-readable way.

FinalAccessor accessor = new FinalAccessor ();
Component component = accessor.provideComponent ();
Reader reader = accessor.provideReader ();

5. Forbid any use of a subclass of ComponentAccessor
Even if the FinalAccessor is final, someone can still extend ReaderAccessor and declare again some provide methods.
You can use a tip to detect if someone is trying to code an accessor on his own by detecting if the package name is right.
Make the ComponentAccessor extend a RootAccessor like this :

package bar;
public abstract class ComponentAccessor extends RootAccessor {
public Component provideComponent (){
return new MyComponent ();
}
}

In the RootAccessor, test the package name and throw an exception if it does not match with what you expect

package bar;
public abstract class RootAccessor (){
public RootAccessor () {
if (!this.getClass ().getPackage ().getName ().startsWith (RootAccessor.class.getPackage ().getName ())) {
throw new MyException ("Not allowed to create an accessor outside the package");
}
}

Well done : you have protected your lib against a too smart client code.
Even the IDE will help you : in the auto-completion tab, you will only see the interface classes (Component, Reader,…) and the Accessors (RootAccessor, ComponentAccessor). The MyOperation, MyComponent or MyReader classes will not be suggested and therefore will not be available outside your project. Any attempt to use the accessor will result in an exception.

My leasure time library project, soundtransform, uses that pattern, you can have a look at https://github.com/libetl/soundtransform/ and see the accessors I wrote (start by looking at my AndroidRootModule and my JavaxRootModule in the IoC part of the source code).