Java:将公共类方法公开给同一个项目的所有包,但对其他项目使用私有方法

时间:2022-09-24 23:37:36

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.

我有一个图书馆项目,有两个包,分别是package1和package2和class1和class2。class1有一些公开给最终用户的方法。我想在class1中添加一些只有class2才能访问的实用方法。我搜索了很多,但是我找不到任何方法的访问修饰符来授予对同一个项目的不同包的访问权限。

It there any chance to achieve it by any means?

有任何机会实现它吗?

UPDATE (Code example):

更新(代码):

Class1 in package1:

Class1 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:

Class2 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
}

4 个解决方案

#1


2  

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:

没有办法做到这一点(如果这是你的来源,那就不像c++的朋友了)。尽管受保护成员可以通过扩展类从不同的包中访问,如下所示:

package1
public Class1{
    protected method();
}

Class2 extends Class1 and hence the method() is visible in Class1 even if Class2 is in a different package.

Class2扩展了Class1,因此即使Class2在不同的包中,Class1中的方法()也是可见的。

package2
public Class2 extends Class1{
    public otherMethod(){
        method(); // is visible here
    }
}

Class3 does not extend Class1 hence method() will not be visible

Class3不扩展Class1,因此方法()将不可见。

 package2
 public Class3{
      public otherMethod(){
          method(); // not visible here
      }
 }

IMO this is the furthest you can go for hiding methods in Class1

在我看来,这是你能在类1中找到隐藏方法的最远距离

#2


2  

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!).

您可以使用默认方法向Class1添加一个公共嵌套接口,这些方法在Class1中调用各自的包访问方法,并在Class2中实现该接口,以便只有Class2能够通过该接口访问Class1的包访问方法(抱歉!)。

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.

接口定义了一个默认方法,该方法为Class1的一个实例,调用该实例的相应方法。我对封闭类和接口方法使用了相同的名称,但是它们可以是不同的。

Note that the interface must be public itself, so it can be visible to Class2 for implementation.

注意,接口本身必须是公共的,因此可以对Class2可见,以便实现。

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.

实现该接口允许访问其默认方法。由于Class2持有Class1的一个实例,所以它在接口方法的所有调用中都被使用。接口将操作委托给Class1实例。

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.

我用不同的int类型实例化2个class1,以便很容易地区分它们。然后,我使用您的给定方法在Class2中设置Class1引用,并在Class2中调用public(公开给用户)方法。在它内部,这个调用通过中介接口调用Class1中的非可访问(非可见)实用程序方法。

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.

注意,在类1的包之外访问类1的实用工具方法的唯一方法是实现中介(您不能从中介本身调用它,因为您不能实例化接口)。因为只有Class2能做到这一点(如果有的话,您也可以控制其他类也能做到这一点),所以只有它能在Class1的包之外访问它。

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.

实际上,您不必将接口作为嵌套接口—这取决于您的整体结构。它可以驻留在自己的编译单元中,但是在与Class1相同的包中,它将具有可见的(包访问)实用程序方法。它是一个嵌套接口的优点是,现在实用程序方法实际上可以是私有的(或受保护的),因此在它们的包中甚至是不可访问的。

I mention this because you specify

我提到这个是因为你指定。

I want to add few utility methods in class1 that only class2 can access.

我想在class1中添加一些只有class2才能访问的实用方法。

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.

但不清楚您是指“只有class2可以访问class1包之外”还是“只有class2可以访问整个包”。您使用了实用方法package-access,因此它提示了第一个选项,但我不确定。

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.

如果不明显,那么这种方法更喜欢扩展类,因为这限制了继承,而实现接口在这方面是“免费的”。

#3


0  

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.

如果您甚至不希望用户看到这个方法,那么您必须创建一个facade(一个公共接口),并且只公开这个facade。不要让用户直接使用实现类。

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

如果您也想要某种安全性(注意,通常可以使用反射来破坏安全性),那么将它与其他答案/评论中建议的解决方案结合起来……

#4


0  

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.

如您所见,您将包p指定为允许访问的包。在方法调用doSometing()或doSometingElse()时,将根据允许的包检查调用类的包。如果相等,则打印授予的访问,否则拒绝访问。您可以创建一个实现此功能的抽象类,每个需要受限制访问的类都可以简单地扩展它并提供允许的包。它也适用于静态方法。

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

#1


2  

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:

没有办法做到这一点(如果这是你的来源,那就不像c++的朋友了)。尽管受保护成员可以通过扩展类从不同的包中访问,如下所示:

package1
public Class1{
    protected method();
}

Class2 extends Class1 and hence the method() is visible in Class1 even if Class2 is in a different package.

Class2扩展了Class1,因此即使Class2在不同的包中,Class1中的方法()也是可见的。

package2
public Class2 extends Class1{
    public otherMethod(){
        method(); // is visible here
    }
}

Class3 does not extend Class1 hence method() will not be visible

Class3不扩展Class1,因此方法()将不可见。

 package2
 public Class3{
      public otherMethod(){
          method(); // not visible here
      }
 }

IMO this is the furthest you can go for hiding methods in Class1

在我看来,这是你能在类1中找到隐藏方法的最远距离

#2


2  

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!).

您可以使用默认方法向Class1添加一个公共嵌套接口,这些方法在Class1中调用各自的包访问方法,并在Class2中实现该接口,以便只有Class2能够通过该接口访问Class1的包访问方法(抱歉!)。

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.

接口定义了一个默认方法,该方法为Class1的一个实例,调用该实例的相应方法。我对封闭类和接口方法使用了相同的名称,但是它们可以是不同的。

Note that the interface must be public itself, so it can be visible to Class2 for implementation.

注意,接口本身必须是公共的,因此可以对Class2可见,以便实现。

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.

实现该接口允许访问其默认方法。由于Class2持有Class1的一个实例,所以它在接口方法的所有调用中都被使用。接口将操作委托给Class1实例。

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.

我用不同的int类型实例化2个class1,以便很容易地区分它们。然后,我使用您的给定方法在Class2中设置Class1引用,并在Class2中调用public(公开给用户)方法。在它内部,这个调用通过中介接口调用Class1中的非可访问(非可见)实用程序方法。

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.

注意,在类1的包之外访问类1的实用工具方法的唯一方法是实现中介(您不能从中介本身调用它,因为您不能实例化接口)。因为只有Class2能做到这一点(如果有的话,您也可以控制其他类也能做到这一点),所以只有它能在Class1的包之外访问它。

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.

实际上,您不必将接口作为嵌套接口—这取决于您的整体结构。它可以驻留在自己的编译单元中,但是在与Class1相同的包中,它将具有可见的(包访问)实用程序方法。它是一个嵌套接口的优点是,现在实用程序方法实际上可以是私有的(或受保护的),因此在它们的包中甚至是不可访问的。

I mention this because you specify

我提到这个是因为你指定。

I want to add few utility methods in class1 that only class2 can access.

我想在class1中添加一些只有class2才能访问的实用方法。

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.

但不清楚您是指“只有class2可以访问class1包之外”还是“只有class2可以访问整个包”。您使用了实用方法package-access,因此它提示了第一个选项,但我不确定。

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.

如果不明显,那么这种方法更喜欢扩展类,因为这限制了继承,而实现接口在这方面是“免费的”。

#3


0  

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.

如果您甚至不希望用户看到这个方法,那么您必须创建一个facade(一个公共接口),并且只公开这个facade。不要让用户直接使用实现类。

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

如果您也想要某种安全性(注意,通常可以使用反射来破坏安全性),那么将它与其他答案/评论中建议的解决方案结合起来……

#4


0  

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.

如您所见,您将包p指定为允许访问的包。在方法调用doSometing()或doSometingElse()时,将根据允许的包检查调用类的包。如果相等,则打印授予的访问,否则拒绝访问。您可以创建一个实现此功能的抽象类,每个需要受限制访问的类都可以简单地扩展它并提供允许的包。它也适用于静态方法。

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