Is there a way to simulate the C++ 'friend'

2019-01-01 02:58发布

I would like to be able to write a Java class in one package which can access non-public methods of a class in another package without having to make it a subclass of the other class. Is this possible?

18条回答
宁负流年不负卿
2楼-- · 2019-01-01 03:12

The 'friend' concept is useful in Java, for example, to separate an API from its implementation. It is common for implementation classes to need access to API class internals but these should not be exposed to API clients. This can be achieved using the 'Friend Accessor' pattern as detailed below:

The class exposed through the API:

package api;

public final class Exposed {
    static {
        // Declare classes in the implementation package as 'friends'
        Accessor.setInstance(new AccessorImpl());
    }

    // Only accessible by 'friend' classes.
    Exposed() {

    }

    // Only accessible by 'friend' classes.
    void sayHello() {
        System.out.println("Hello");
    }

    static final class AccessorImpl extends Accessor {
        protected Exposed createExposed() {
            return new Exposed();
        }

        protected void sayHello(Exposed exposed) {
            exposed.sayHello();
        }
    }
}

The class providing the 'friend' functionality:

package impl;

public abstract class Accessor {

    private static Accessor instance;

    static Accessor getInstance() {
        Accessor a = instance;
        if (a != null) {
            return a;
        }

        return createInstance();
    }

    private static Accessor createInstance() {
        try {
            Class.forName(Exposed.class.getName(), true, 
                Exposed.class.getClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }

        return instance;
    }

    public static void setInstance(Accessor accessor) {
        if (instance != null) {
            throw new IllegalStateException(
                "Accessor instance already set");
        }

        instance = accessor;
    }

    protected abstract Exposed createExposed();

    protected abstract void sayHello(Exposed exposed);
}

Example access from a class in the 'friend' implementation package:

package impl;

public final class FriendlyAccessExample {
    public static void main(String[] args) {
        Accessor accessor = Accessor.getInstance();
        Exposed exposed = accessor.createExposed();
        accessor.sayHello(exposed);
    }
}
查看更多
倾城一夜雪
3楼-- · 2019-01-01 03:16

I think, the approach of using the friend accessor pattern is way too complicated. I had to face the same problem and I solved using the good, old copy constructor, known from C++, in Java:

public class ProtectedContainer {
    protected String iwantAccess;

    protected ProtectedContainer() {
        super();
        iwantAccess = "Default string";
    }

    protected ProtectedContainer(ProtectedContainer other) {
        super();
        this.iwantAccess = other.iwantAccess;
    }

    public int calcSquare(int x) {
        iwantAccess = "calculated square";
        return x * x;
    }
}

In your application you could write the following code:

public class MyApp {

    private static class ProtectedAccessor extends ProtectedContainer {

        protected ProtectedAccessor() {
            super();
        }

        protected PrivateAccessor(ProtectedContainer prot) {
            super(prot);
        }

        public String exposeProtected() {
            return iwantAccess;
        }
    }
}

The advantage of this method is that only your application has access to the protected data. It's not exactly a substitution of the friend keyword. But I think it's quite suitable when you write custom libraries and you need to access protected data.

Whenever you have to deal with instances of ProtectedContainer you can wrap your ProtectedAccessor around it and you gain access.

It also works with protected methods. You define them protected in your API. Later in your application you write a private wrapper class and expose the protected method as public. That's it.

查看更多
大哥的爱人
4楼-- · 2019-01-01 03:16

As of Java 9, modules can be used to make this a non-issue in many cases.

查看更多
临风纵饮
5楼-- · 2019-01-01 03:19

As far as I know, it is not possible.

Maybe, You could give us some more details about Your design. Questions like these are likely the result of design flaws.

Just consider

  • Why are those classes in different packages, if they are so closely related?
  • Has A to access private members of B or should the operation be moved to class B and triggered by A?
  • Is this really calling or is event-handling better?
查看更多
低头抚发
6楼-- · 2019-01-01 03:19

In Java it is possible to have a "package-related friendness". This can be userful for unit testing. If you do not specify private/public/protected in front of a method, it will be "friend in the package". A class in the same package will be able to access it, but it will be private outside the class.

This rule is not always known, and it is a good approximation of a C++ "friend" keyword. I find it a good replacement.

查看更多
时光乱了年华
7楼-- · 2019-01-01 03:19

I once saw a reflection based solution that did "friend checking" at runtime using reflection and checking the call stack to see if the class calling the method was permitted to do so. Being a runtime check, it has the obvious drawback.

查看更多
登录 后发表回答