【问题标题】:Java: Expose public class method to all packages of same project but make private for other projectsJava:将公共类方法公开给同一项目的所有包,但对其他项目设为私有
【发布时间】:2015-08-02 13:41:03
【问题描述】:

我有一个库项目,其中包含两个包,分别是带有 class1 和 class2 的 package1 和 package2。 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
}

【问题讨论】:

  • Java 语言甚至没有项目的概念。
  • 受保护的方法具有跨包的可见性,但您必须 extend Class1 from Class2
  • 您是否在寻找等效于 C++ friend 的声明?请参阅有关罗密欧与朱丽叶的旧故事的以下美丽答案:-) stackoverflow.com/a/18634125/1857897
  • @user1803551 两个类都是公共的,具有非静态公共方法,我已经用类结构示例更新了这个问题。
  • @dedek,这是一个很好的java朋友类实现。但是由于我正在开发一个库,因此即使库用户无法使用该方法,他们仍然可以看到该方法。我什至不想让他们展示方法,这会让他们感到困惑。 :)

标签: java oop design-patterns access-modifiers


【解决方案1】:

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

package1
public Class1{
    protected method();
}

Class2 extends Class1,因此 method()Class1 中可见,即使 Class2 位于不同的包中。

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 中隐藏方法的最远距离

【讨论】:

    【解决方案2】:

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

    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
    

    【讨论】:

    • 您将类声明为static,这意味着它是某个地方的嵌套类。在哪里?
    • 这就是我测试它的方式。现在更新答案。
    【解决方案3】:

    您可以使用default 方法将public 嵌套接口添加到Class1,这些方法在Class1implement 中调用它们各自的包访问方法@Class2 中的该接口,这样只有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 个Class1s 只是为了轻松区分它们。然后我使用您给定的方法在Class2 中设置Class1 引用,并在Class2 中调用public(向用户公开)方法。这个调用,在它里面,通过Mediator接口调用Class1中不可访问(不可见)的实用方法。

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

    上面运行的输出是

    3
    4
    

    为什么是嵌套接口?

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

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

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

    但不清楚您的意思是“只有 class2 可以访问 class1 的包之外”还是“只有 class2 可以访问整体”。您创建了实用程序方法包访问,所以它提示第一个选项,但我不确定。

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

    最后说明

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

    【讨论】:

      【解决方案4】:

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

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

      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();
      

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

      【讨论】:

        猜你喜欢
        • 2011-06-21
        • 1970-01-01
        • 2011-07-12
        • 1970-01-01
        • 2017-05-30
        • 1970-01-01
        • 2021-11-20
        • 2023-04-07
        相关资源
        最近更新 更多