繁体   English   中英

Java:将公共类方法暴露给同一项目的所有包,但为其他项目设置私有

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

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

有机会以任何方式实现它吗?

更新(代码示例):

package1中的Class1:

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

package2中的class2:

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
}

没有办法实现这一点(如果你来自哪里,就没有像C ++中的friend那样)。 虽然protected成员可以通过扩展类从不同的包访问,如下所示:

package1
public Class1{
    protected method();
}

Class2 extends Class1 ,因此即使Class2在不同的包中, method()Class1也是可见的。

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

Class3不扩展Class1因此method()不可见

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

IMO这是你在Class1中隐藏方法最远的地方

您可以使用default方法向Class1添加public嵌套接口,这些方法在Class1调用它们各自的包访问方法,并在Class2 implement该接口,以便只有Class2可以通过该接口访问Class1的包访问方法(抱歉!)。

在这一点上可能更好地显示代码。

Class1

我为该方法添加了一些哑打印实现,以显示它被正确调用。

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

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

请注意,接口必须是public ,因此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
}

实现接口允许访问其default方法。 由于Class2拥有Class1的实例,因此它(将)用于接口方法的所有调用。 接口将操作委托给Class1实例。

UserClass

我在自己的包中添加了这个类作为实例化类并调用各种方法的地方。 我不确定在你的情况下是怎么做的,但是熨平细节应该不是问题。

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

我使用不同的int实例化2个Class1 ,以便轻松区分它们。 然后,我用你给的方法来设置Class1参考Class2并调用public (暴露给用户)的方法Class2 其中的此调用通过Mediator接口调用Class1的不可访问(不可见)实用程序方法。

请注意,在其程序包之外访问Class1的实用程序方法的唯一方法是实现Mediator (您无法从Mediator本身调用它,因为您无法实例化接口)。 因为只有Class2这样做(并且你可以控制哪些其他类也可以,如果有的话),只有它可以在Class1的包之外访问它。

运行上面的输出是

3
4

为什么嵌套接口?

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

我提到这是因为你指定了

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

但是不清楚你是否意味着“只有class2才能访问class1的包之外”或“只有class2可以访问整体”。 你使用了实用程序方法package-access,所以它提示了第一个选项,但我不确定。

还有设计考虑“这是放置这个界面的正确位置吗?”,但我不知道 - 你可以。 嵌套接口通常遵循嵌套类的相同设计考虑因素,因此您可以依赖它。

最后的说明

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

我将以下解决方法放在一起,确保只有指定的包可以访问某些方法:

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");
        }
    }
} 

如您所见,您将Package p指定为允许访问的包。 在方法调用doSometing()doSometingElse() ,将根据允许的包检查调用类的包。 如果相等, Access granted被打印出来,并Access denied其他。 您可以创建一个实现此功能的abstract类,并且每个需要受限访问的类都可以简单地扩展它并提供允许的包。 它也适用于静态方法。

让我们创建两个将尝试访问Reserved方法的类:

package a.b.c;

public class AccesserA {
    public void tryAccess() throws ClassNotFoundException {
        Reserved res = new Reserved();
        res.doSomething();
    }
}

package a.b;

public class AccesserB {
    public void tryAccess() throws ClassNotFoundException {
        Reserved res = new Reserved();
        res.doSomething();
        Legit.Reserved.doSometingElse();
    }
}

主要:

public static void main (String ... args) throws IOException, InterruptedException, ClassNotFoundException {
    AccesserA a = new AccesserA();
    AccesserB b = new AccesserB();
    a.tryAccess();
    b.tryAccess();
}

这将产生输出:

Access granted
Access denied
Access denied

如果您甚至不希望用户看到该方法,则必须创建一个Facade(公共接口)并仅公开此Facade。 不要让用户直接使用实现类。

这通常使用工厂或工厂方法+(如果需要)将所有敏感构造函数设为私有。

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

然后,无论您想使用实用程序方法,都必须使用强制转换:

Interface1 class1_instance = Class1.create();

((Class1) class1_instance).performUtilityOperation();

如果你想要某种安全性(请注意,通常可以使用反射来破坏它),然后将其与其他答案/评论中建议的解决方案相结合......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM