我希望能够在一个包中编写一个Java类,它可以访问另一个包中类的非公共方法,而不必使它成为另一个类的子类。这可能吗?


当前回答

提供的解决方案可能不是最简单的。另一种方法基于与c++相同的思想:除了所有者与自己为友的特定类外,在包/私有作用域之外不能访问私有成员。

需要对成员进行友元访问的类应该创建一个内部公共抽象“友元类”,拥有隐藏属性的类可以通过返回实现访问实现方法的子类导出对该类的访问。友元类的“API”方法可以是私有的,因此在需要友元访问的类外部无法访问它。它的唯一语句是对导出类实现的抽象受保护成员的调用。

代码如下:

首先是验证它是否实际工作的测试:

package application;

import application.entity.Entity;
import application.service.Service;
import junit.framework.TestCase;

public class EntityFriendTest extends TestCase {
    public void testFriendsAreOkay() {
        Entity entity = new Entity();
        Service service = new Service();
        assertNull("entity should not be processed yet", entity.getPublicData());
        service.processEntity(entity);
        assertNotNull("entity should be processed now", entity.getPublicData());
    }
}

然后,需要友方访问Entity的包私有成员的服务:

package application.service;

import application.entity.Entity;

public class Service {

    public void processEntity(Entity entity) {
        String value = entity.getFriend().getEntityPackagePrivateData();
        entity.setPublicData(value);
    }

    /**
     * Class that Entity explicitly can expose private aspects to subclasses of.
     * Public, so the class itself is visible in Entity's package.
     */
    public static abstract class EntityFriend {
        /**
         * Access method: private not visible (a.k.a 'friendly') outside enclosing class.
         */
        private String getEntityPackagePrivateData() {
            return getEntityPackagePrivateDataImpl();
        }

        /** contribute access to private member by implementing this */
        protected abstract String getEntityPackagePrivateDataImpl();
    }
}

最后:Entity类,它仅对类application.service.Service提供对包私有成员的友好访问。

package application.entity;

import application.service.Service;

public class Entity {

    private String publicData;
    private String packagePrivateData = "secret";   

    public String getPublicData() {
        return publicData;
    }

    public void setPublicData(String publicData) {
        this.publicData = publicData;
    }

    String getPackagePrivateData() {
        return packagePrivateData;
    }

    /** provide access to proteced method for Service'e helper class */
    public Service.EntityFriend getFriend() {
        return new Service.EntityFriend() {
            protected String getEntityPackagePrivateDataImpl() {
                return getPackagePrivateData();
            }
        };
    }
}

好吧,我必须承认它比“friend service:: service;”稍微长一点,但是可以在使用注释保留编译时检查的同时缩短它。

其他回答

在Java中,有可能具有“包相关的友好性”。 这对于单元测试很有用。 如果你在一个方法前没有指定private/public/protected,它将是“friend in the package”。 同一个包中的类将能够访问它,但它在类外部是私有的。

这个规则并不总是为人所知,它很好地近似于c++的“朋友”关键字。 我觉得这是一个很好的替代品。

如果你想访问受保护的方法,你可以创建一个你想要使用的类的子类,它将你想要使用的方法公开为公共的(或者是更安全的命名空间内部的),并在你的类中有一个类的实例(将它用作代理)。

就私人方法而言(我认为)你运气不好。

据我所知,这是不可能的。

也许,你可以给我们更多关于你的设计的细节。像这样的问题很可能是设计缺陷造成的。

只考虑

如果这些类如此密切相关,为什么它们在不同的包中? A是否访问B的私有成员,或者操作是否应该移动到类B并由A触发? 这是真正的调用还是事件处理更好?

对于您的问题,有两种解决方案不涉及将所有类保存在同一个包中。

第一种是使用(实用API设计,Tulach 2008)中描述的朋友访问器/朋友包模式。

第二种是使用OSGi。这里有一篇文章解释了OSGi是如何实现这一点的。

相关问题:1、2和3。

提供的解决方案可能不是最简单的。另一种方法基于与c++相同的思想:除了所有者与自己为友的特定类外,在包/私有作用域之外不能访问私有成员。

需要对成员进行友元访问的类应该创建一个内部公共抽象“友元类”,拥有隐藏属性的类可以通过返回实现访问实现方法的子类导出对该类的访问。友元类的“API”方法可以是私有的,因此在需要友元访问的类外部无法访问它。它的唯一语句是对导出类实现的抽象受保护成员的调用。

代码如下:

首先是验证它是否实际工作的测试:

package application;

import application.entity.Entity;
import application.service.Service;
import junit.framework.TestCase;

public class EntityFriendTest extends TestCase {
    public void testFriendsAreOkay() {
        Entity entity = new Entity();
        Service service = new Service();
        assertNull("entity should not be processed yet", entity.getPublicData());
        service.processEntity(entity);
        assertNotNull("entity should be processed now", entity.getPublicData());
    }
}

然后,需要友方访问Entity的包私有成员的服务:

package application.service;

import application.entity.Entity;

public class Service {

    public void processEntity(Entity entity) {
        String value = entity.getFriend().getEntityPackagePrivateData();
        entity.setPublicData(value);
    }

    /**
     * Class that Entity explicitly can expose private aspects to subclasses of.
     * Public, so the class itself is visible in Entity's package.
     */
    public static abstract class EntityFriend {
        /**
         * Access method: private not visible (a.k.a 'friendly') outside enclosing class.
         */
        private String getEntityPackagePrivateData() {
            return getEntityPackagePrivateDataImpl();
        }

        /** contribute access to private member by implementing this */
        protected abstract String getEntityPackagePrivateDataImpl();
    }
}

最后:Entity类,它仅对类application.service.Service提供对包私有成员的友好访问。

package application.entity;

import application.service.Service;

public class Entity {

    private String publicData;
    private String packagePrivateData = "secret";   

    public String getPublicData() {
        return publicData;
    }

    public void setPublicData(String publicData) {
        this.publicData = publicData;
    }

    String getPackagePrivateData() {
        return packagePrivateData;
    }

    /** provide access to proteced method for Service'e helper class */
    public Service.EntityFriend getFriend() {
        return new Service.EntityFriend() {
            protected String getEntityPackagePrivateDataImpl() {
                return getPackagePrivateData();
            }
        };
    }
}

好吧,我必须承认它比“friend service:: service;”稍微长一点,但是可以在使用注释保留编译时检查的同时缩短它。