Reputation: 354
I have a library project with two packages say package1 and package2 with class1 and class2 respectively. class1 has some public methods exposed to end user. I want to add few utility methods in class1 that only class2 can access. I searched a lot but I couldn't find any access modifier for method to grant access across different packages of same project only.
It there any chance to achieve it by any means?
UPDATE (Code example):
Class1 in package1:
package com.example.package1;
public class Class1 {
// This method should only be accessed by Class2, cannot made it public for
// every class
void performUtilityOperation() {
// internal utility
}
// other public methods...
}
Class2 in package2:
package com.example.package2;
import com.example.package1.*;
public class Class2 {
Class1 class1;
public void setClass1(Class1 class1) {
this.class1 = class1;
}
public void doSomeOperation() {
this.class1.performUtilityOperation(); // here this method is not
// accessible if not public
// do some other operations
}
// other public methods
}
Upvotes: 12
Views: 5336
Reputation: 2701
I put the following workaround together, that ensures only specified packages can access certain methods:
public class Reserved {
private static Package p = AccesserA.class.getPackage();
public void doSomething() throws ClassNotFoundException {
if (p.equals(Class.forName(Thread.currentThread().getStackTrace()[2].getClassName()).getPackage())) {
System.out.println("Access granted");
} else {
System.out.println("Access denied");
}
}
public static void doSometingElse() throws ClassNotFoundException {
if (p.equals(Class.forName(Thread.currentThread().getStackTrace()[2].getClassName()).getPackage())) {
System.out.println("Access granted");
} else {
System.out.println("Access denied");
}
}
}
As you can see, you specify Package p
as the package that is allowed access. Upon method call doSometing()
or doSometingElse()
the package of the calling class is checked against the allowed package. If it is equal, Access granted
is printed, and Access denied
otherwise. You can maybe create a abstract
class that implements this, and every class that requires restricted access can simply extend it and provide the allowed packages. It works for static methods as well.
Lets create two classes that will attempt to access Reserved
methods:
package a.b.c;
public class AccesserA {
public void tryAccess() throws ClassNotFoundException {
Reserved res = new Reserved();
res.doSomething();
}
}
and
package a.b;
public class AccesserB {
public void tryAccess() throws ClassNotFoundException {
Reserved res = new Reserved();
res.doSomething();
Legit.Reserved.doSometingElse();
}
}
Main:
public static void main (String ... args) throws IOException, InterruptedException, ClassNotFoundException {
AccesserA a = new AccesserA();
AccesserB b = new AccesserB();
a.tryAccess();
b.tryAccess();
}
This will produce output:
Access granted
Access denied
Access denied
Upvotes: 0
Reputation: 8311
If you don't even want users to see the method, you have to create a facade (a public interface) and expose only this facade. Do not let users to work directly with implementation classes.
This is usually done using factories or factory methods + (if you want) making all the sensitive constructors private.
public class Class1 implements Interface1 {
private Class1() {}
public static Interface1 create() { return new Class1(); }
// This method is not visible through Interface1
public void performUtilityOperation() {
// internal utility
}
}
Then, wherever you want to use the utility method, you have to use casting:
Interface1 class1_instance = Class1.create();
((Class1) class1_instance).performUtilityOperation();
If you want some sort of security as well (note that it is usually possible to break it using reflection) then combine it with solutions suggested in other answers / comments...
Upvotes: 0
Reputation: 13427
You can add a public
nested interface to Class1
with default
methods which call their respective package-access methods in Class1
and implement
that interface in Class2
so that only Class2
gains access to Class1
's package-access methods through that interface (sorry!).
Probably better at this point to show the code.
Class1
I added some dumb printing implementation for the method to show that it is being called properly.
package package1;
public class Class1 {
int i;
public Class1(int i) {
this.i = i;
}
// Utility method only for Class2
void performUtilityOperation() {
System.out.println(i);
}
public interface Mediator {
default void performUtilityOperation(Class1 c1) {
c1.performUtilityOperation();
}
}
// other public methods...
}
The interface defines a default
method, which given an instance of Class1
, calls that instance's respective method. I used the same names for the enclosing class and interface methods, but they can be different.
Note that the interface must be public
itself, so it can be visible to Class2
for implementation.
Class2
package package2;
import package1.Class1;
public class Class2 implements Class1.Mediator {
Class1 class1;
public void setClass1(Class1 class1) {
this.class1 = class1;
}
public void doSomeOperation() {
performUtilityOperation(class1);
}
// other public methods
}
Implementing the interface allows access to its default
methods. Since Class2
holds an instance of Class1
, it is (to be) used in all invocations of the interface methods. The interface delegates the operations to the Class1
instance.
UserClass
I added this class in its own package as a place to instantiate the classes and call the various methods. I'm not sure how it is intended to be done in your case, but ironing out the details should not be a problem.
package user;
import package1.Class1;
import package2.Class2;
class UserClass {
public static void main(String[] args) {
Class1 clazz1Int3 = new Class1(3);
Class1 clazz1Int4 = new Class1(4);
Class2 c2 = new Class2();
c2.setClass1(clazz1Int3);
c2.doSomeOperation();
c2.setClass1(clazz1Int4);
c2.doSomeOperation();
// clazz1Int3.performUtilityOperation(); // The method performUtilityOperation() from the type Class1 is not visible
}
}
I instantiate 2 Class1
s with a different int
just to distinguish between them easily. I then use your given method to set the Class1
reference in Class2
and call the public
(exposed to the user) method in Class2
. This call, inside it, calls the non-accessible (non-visible) utility method in Class1
through the Mediator
interface.
Note that the only way to access Class1
's utility method outside of its package is to implement Mediator
(you can't call it from Mediator
itself because you can't instantiate an interface). Since only Class2
does that (and you can control which other classes do it as well, if at all), only it can access it outside of Class1
's package.
The output for running the above is
3
4
Actually, you don't have to put the interface as a nested interface - it depends on your overall structure. It can reside in its own compilation unit, but in the same package as Class1
so it will have the (package access) utility methods visible. The advantage of it being a nested interface is that now the utility methods can actually be private
(or protected
) and thus not even accessible in their package.
I mention this because you specify
I want to add few utility methods in class1 that only class2 can access.
but it is not clear if you mean "only class2 can access outside of class1's package" or "only class2 can access overall". You made the utility method package-access, so it hints to the first option, but I wasn't sure.
There is also the design consideration of "is this the right place to put this interface?", but I can't know that - you can. Nested interfaces generally follow the same design considerations a nested classes, so you have that to rely upon.
If it was not obvious, then this approach is preferred to extending classes since that restricts your inheritance, while implementing interfaces is "free" in that regard.
Upvotes: 2
Reputation: 1607
There is no way to achieve this(nothing like friend
in C++ if that's where u r coming from). Although protected
members are accessible from a different package by an extending class as shown below:
package1
public Class1{
protected method();
}
Class2 extends Class1
and hence the method()
is visible in Class1
even if Class2 is in a different package.
package2
public Class2 extends Class1{
public otherMethod(){
method(); // is visible here
}
}
Class3
does not extend Class1
hence method()
will not be visible
package2
public Class3{
public otherMethod(){
method(); // not visible here
}
}
IMO this is the furthest you can go for hiding methods in Class1
Upvotes: 2