I have two packages in my project: odp.proj
and odp.proj.test
. There are certain methods that I want to be visible only to the classes in these two packages. How can I do this?
EDIT: If there is no concept of a subpackage in Java, is there any way around this? I have certain methods that I want to be available only to testers and other members of that package. Should I just throw everything into the same package? Use extensive reflection?
You can't. In Java there is no concept of a subpackage, so odp.proj and odp.proj.test are completely separate packages.
The names of your packages hint that the application here is for unit testing. The typical pattern used is to put the classes you wish to test and the unit test code in the same package (in your case odp.proj
) but in different source trees. So you would put your classes in src/odp/proj
and your test code in test/odp/proj
.
Java does have the "package" access modifier which is the default access modifier when none is specified (ie. you don't specify public, private or protected). With the "package" access modifier, only classes in odp.proj
will have access to the methods. But keep in mind that in Java, the access modifiers cannot be relied upon to enforce access rules because with reflection, any access is possible. Access modifiers are merely suggestive (unless a restrictive security manager is present).
This is no special relation between odp.proj
and odp.proj.test
- they just happen to be named as apparently related.
If the odp.proj.test package is simply providing tests then you can use the same package name (odp.proj
). IDEs like Eclipse and Netbeans will create separate folders (src/main/java/odp/proj
and src/test/java/odp/proj
) with the same package name but with JUnit semantics.
Note that these IDEs will generate tests for methods in odp.proj
and create the appropriate folder for the test methods it doesn't exist.
When I do this in IntelliJ, my source tree looks like this:
src // source root
- odp
- proj // .java source here
- test // test root
- odp
- proj // JUnit or TestNG source here
EDIT: If there is no concept of a
subpackage in Java, is there any way
around this? I have certain methods
that I want to be available only to
testers and other members of that
package.
It probably depends a bit on your motives for not displaying them but if the only reason is that you don't want to pollute the public interface with the things intended only for testing (or some other internal thing) I would put the methods in a separate public interface and have the consumers of the "hidden" methods use that interface. It will not stop others from using the interface but I see no reason why you should.
For unit tests, and if it is possible without rewriting the lot, follow the suggestions to use the same package.
As others have explained, there is no such thing as a "subpackage" in Java: all packages are isolated and inherit nothing from their parents.
An easy way to access protected class members from another package is to extend the class and override the members.
For instance, to access ClassInA
in package a.b
:
package a;
public class ClassInA{
private final String data;
public ClassInA(String data){ this.data = data; }
public String getData(){ return data; }
protected byte[] getDataAsBytes(){ return data.getBytes(); }
protected char[] getDataAsChars(){ return data.toCharArray(); }
}
make a class in that package that overrides the methods you need in ClassInA
:
package a.b;
import a.ClassInA;
public class ClassInAInB extends ClassInA{
ClassInAInB(String data){ super(data); }
@Override
protected byte[] getDataAsBytes(){ return super.getDataAsBytes(); }
}
That lets you use the overriding class in place of the class in the other package:
package a.b;
import java.util.Arrays;
import a.ClassInA;
public class Driver{
public static void main(String[] args){
ClassInA classInA = new ClassInA("string");
System.out.println(classInA.getData());
// Will fail: getDataAsBytes() has protected access in a.ClassInA
System.out.println(Arrays.toString(classInA.getDataAsBytes()));
ClassInAInB classInAInB = new ClassInAInB("string");
System.out.println(classInAInB.getData());
// Works: getDataAsBytes() is now accessible
System.out.println(Arrays.toString(classInAInB.getDataAsBytes()));
}
}
Note that this only works for protected members, which are visible to extending classes, and not package-private members, which aren't. Hopefully this helps someone!
Without putting the access modifier in front of the method you say that it is package private.
Look at the following example.
package odp.proj;
public class A
{
void launchA() { }
}
package odp.proj.test;
public class B
{
void launchB() { }
}
public class Test
{
public void test()
{
A a = new A();
a.launchA() // cannot call launchA because it is not visible
}
}
With the PackageVisibleHelper class, and keep it private before PackageVisibleHelperFactory frozen, we can invoke the launchA(by PackageVisibleHelper ) method in anywhere:)
package odp.proj;
public class A
{
void launchA() { }
}
public class PackageVisibleHelper {
private final PackageVisibleHelperFactory factory;
public PackageVisibleHelper(PackageVisibleHelperFactory factory) {
super();
this.factory = factory;
}
public void launchA(A a) {
if (factory == PackageVisibleHelperFactory.INSTNACNE && !factory.isSampleHelper(this)) {
throw new IllegalAccessError("wrong PackageVisibleHelper ");
}
a.launchA();
}
}
public class PackageVisibleHelperFactory {
public static final PackageVisibleHelperFactory INSTNACNE = new PackageVisibleHelperFactory();
private static final PackageVisibleHelper HELPER = new PackageVisibleHelper(INSTNACNE);
private PackageVisibleHelperFactory() {
super();
}
private boolean frozened;
public PackageVisibleHelper getHelperBeforeFrozen() {
if (frozened) {
throw new IllegalAccessError("please invoke before frozen!");
}
return HELPER;
}
public void frozen() {
frozened = true;
}
public boolean isSampleHelper(PackageVisibleHelper helper) {
return HELPER.equals(helper);
}
}
package odp.proj.test;
import odp.proj.A;
import odp.proj.PackageVisibleHelper;
import odp.proj.PackageVisibleHelperFactory;
public class Test {
public static void main(String[] args) {
final PackageVisibleHelper helper = PackageVisibleHelperFactory.INSTNACNE.getHelperBeforeFrozen();
PackageVisibleHelperFactory.INSTNACNE.frozen();
A a = new A();
helper.launchA(a);
// illegal access
new PackageVisibleHelper(PackageVisibleHelperFactory.INSTNACNE).launchA(a);
}
}