1. ホーム

[解決済み】C++の「友達」コンセプトをJavaでシミュレートする方法はありますか?

2022-04-06 23:09:35

質問

あるパッケージのJavaクラスが、他のパッケージのクラスの非公開メソッドにアクセスできるようにしたいのですが、そのクラスを他のクラスのサブクラスとする必要があります。これは可能ですか?

どのように解決するのですか?

フレンド」という概念は、JavaではAPIとその実装を分離する場合などに便利です。実装クラスがAPIクラス内部へのアクセスを必要とすることはよくありますが、これらはAPIクライアントには公開されるべきではありません。これは、以下に説明する「フレンドアクセサ」パターンを使うことで実現できる。

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

friend' 機能を提供するクラスです。

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

friend'実装パッケージのクラスからのアクセス例です。

package impl;

public final class FriendlyAccessExample {
    public static void main(String[] args) {
        Accessor accessor = Accessor.getInstance();
        Exposed exposed = accessor.createExposed();
        accessor.sayHello(exposed);
    }
}