Java (C++ など) でフレンドの概念をどのように実装しますか?
4 に答える
Java には、C++ のフレンド キーワードはありません。ただし、それをエミュレートする方法があります。実際にはより正確な制御を提供する方法です。クラス A と B があるとします。B は、A のプライベート メソッドまたはフィールドにアクセスする必要があります。
public class A {
private int privateInt = 31415;
public class SomePrivateMethods {
public int getSomethingPrivate() { return privateInt; }
private SomePrivateMethods() { } // no public constructor
}
public void giveKeyTo(B other) {
other.receiveKey(new SomePrivateMethods());
}
}
public class B {
private A.SomePrivateMethods key;
public void receiveKey(A.SomePrivateMethods key) {
this.key = key;
}
public void usageExample() {
A anA = new A();
// int foo = anA.privateInt; // doesn't work, not accessible
anA.giveKeyTo(this);
int fii = key.getSomethingPrivate();
System.out.println(fii);
}
}
usageExample() は、これがどのように機能するかを示しています。B のインスタンスは、A のインスタンスのプライベート フィールドまたはメソッドにアクセスできません。しかし、giveKeyTo() を呼び出すことにより、クラス B はアクセスを取得できます。引数として有効な B が必要なため、他のクラスはそのメソッドにアクセスできません。コンストラクターはプライベートです。
クラス B は、キーで渡された任意のメソッドを使用できます。これは、C++ の friend キーワードより設定が面倒ですが、はるかにきめ細かく設定できます。クラス A は、どのメソッドをどのクラスに公開するかを正確に選択できます。
上記のケースでは、A は B のすべてのインスタンスと B のサブクラスのインスタンスへのアクセスを許可しています。後者が望ましくない場合、giveKeyTo() メソッドは getClass() を使用して other の正確なタイプを内部的にチェックし、throw します。正確に B でない場合は例外です。
によってのみ呼び出さA.foo()
れるとしB
ます。これは、 によってのみ生成できるトークンによって調整できますB
。
public class B
{
public static class ToA { private ToA(){} }
private static final ToA b2a = new ToA();
void test()
{
new A().foo(b2a);
}
}
public class A
{
public void foo(B.ToA b2a)
{
if(b2a==null)
throw new Error("you ain't B");
// ...
}
}
B
null 以外のB.ToA
トークンのみを生成できます。A
との両方B
がこのトークンをサードパーティに漏らさない場合、他の誰も呼び出すことができませんA.foo()
A2
友達になりたい場合B
は、別のトークン タイプが必要です。同じトークン タイプの場合、 は からA
タイプのトークンを取得したため、 のB
ふりA
をすることができB
ますA2
。
チェックはコンパイル時ではなく実行時に行われるため、完全ではありません。ただし、大したことではありません。サードパーティは でしか呼び出すことA.foo()
がnull
できないため、コンパイル時にチェックしたい無害な間違いではありません。おそらく悪意があるため、コンパイル時に呼び出し元に警告する必要はありません。
Java では、両方 (またはそれ以上) のクラスを同じパッケージに入れることができます。修飾子を持つすべてのメソッドとフィールドは、protected
そのパッケージ内のすべてのクラスから直接アクセスできます。
同じことを達成する別の方法を考え出しました。基本的に、呼び出しクラス名の完全修飾名を確認します。「フレンド」関数と一致する場合はアクセスを許可し、それ以外の場合は null を返します。
public class A {
private static int privateInt = 31415;
public static int getPrivateInt() {
if(Throwable().getStackTrace()[1].getClassName().equals(new String("example.java.testing.B")))
{
return privateInt;
}
else
{
return null;
}
}
}
package example.java.testing;
public class B {
public void usageExample() {
int foo = A.getPrivateInt; // works only for B
System.out.println(foo);
}
}