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

时间:2015-05-21 04:07:11

标签: java oop design-patterns access-modifiers

我有一个包含两个软件包的库项目,分别是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
}

4 个答案:

答案 0 :(得分:2)

没有办法实现这一点(如果你来自哪里,那就像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

中隐藏方法的最远距离

答案 1 :(得分:2)

您可以使用public方法向Class1添加default嵌套接口,这些方法在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
    }
}

我使用不同的Class1实例化2 int只是为了轻松区分它们。然后,我使用您的给定方法在Class1中设置Class2引用,并在public中调用 Class2 (向用户公开)方法。其中的此调用通过Class1接口调用Mediator中的不可访问(不可见)实用程序方法。

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

运行上述内容的输出是

Class1

为什么嵌套界面?

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

我提到这是因为你指定了

  

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

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

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

最后的注释

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

如果您甚至不希望用户看到该方法,则必须创建一个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();

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