Naheed Sultana
Naheed Sultana

Reputation: 354

Java: Expose public class method to all packages of same project but make private for other projects

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

Answers (4)

Ian2thedv
Ian2thedv

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

dedek
dedek

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

user1803551
user1803551

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 Class1s 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

Why the nested interface?

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.

Final note

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

Nitin Dandriyal
Nitin Dandriyal

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

Related Questions