2013-01-09 15 views
5

C++のような、Javaのフレンドコンセプトをどのように実装しますか?JavaのFriendコンセプトの実装

+0

私がC++でコーディングしても、私は 'friend'を非常に控えめに使っています。すべてで使用できる公開インタフェースを提供する方がはるかに安全です。 –

+0

私は自分のコードでJavaの友人概念を使用していません。私は好奇心のためだけに好奇心旺盛でした。 –

+0

@ Code-Guruパブリックインターフェイスはどのように役立つでしょうか?私はここで何かを逃しているかもしれない。もっと詳しい情報を提供するリンクを投稿できますか? –

答えて

23

JavaにはC++のフレンドキーがありません。しかし、それをエミュレートする方法があります。実際よりはるかに正確な制御を与える方法です。あなたはAとB B usageExample(

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

A.にいくつかのプライベートメソッドまたはフィールドへのアクセスを必要とするが)、これはどのように動作するかを示したクラスがあるとします。 Bのインスタンスは、Aのインスタンスのプライベートフィールドまたはメソッドにアクセスすることはできません。しかし、giveKeyTo()を呼び出すと、クラスBがアクセスできます。有効なBを引数として必要とするため、他のクラスはそのメソッドにアクセスできません。コンストラクタはprivateです。

クラスBは、キーに渡されたメソッドのいずれかを使用できます。これは、C++の友人キーワードよりもセットアップが煩雑ではあるが、はるかにきめ細かくなっている。クラスAは、どのメソッドを正確にどのクラスに公開するかを選択することができます。

ここで、AはBのすべてのインスタンスとBのサブクラスのインスタンスへのアクセスを許可します。後者が望ましくない場合、giveKeyTo()メソッドは内部的にotherの正確な型をgetClass )、正確でない場合は例外をスローします。

+0

非常に良い。これを実装するより良い方法は、潜在的なセキュリティリスクを回避して、クラス 'B'を' final'として宣言することでしょう。 – user991710

1

Javaでは、両方の(またはそれ以上の)クラスを同じパッケージに入れることができます。 protected修飾子を持つすべてのメソッドとフィールドは、そのパッケージ内のすべてのクラスから直接アクセスできます。

+0

しかし、既存のレガシーコードがすでに存在し、リファクタリングが面倒になり、フィールドやメソッドの可視性を変更したくない場合、そのような友人関係を設定することはオプションになる可能性があります。私は同意する、それは非常に読みやすく、推奨されませんが、それは迅速な修正として動作します。 –

+0

私は、これが永久に一時的な、または一時的な永久的なオプションになったときに、オプションの、最悪の場合、技術的な借金のサブプライムレベルへようこそ最後の溝を指摘する必要があると感じています。 –

+0

私が知っているほとんどすべての人が、影響を受けるメソッドをpublicとして定義していますが、あなたのアプローチは面白いです。 – AlexWien

-2

私は同じことを達成する別の方法を考え出しました。基本的には、呼び出すクラス名の完全修飾名をチェックします。それがあなたの "友人"の機能と一致すれば、あなたはアクセス権を与えます。それ以外の場合は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); 
    } 
} 
3

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トークンを生成することができます。 A2があまりにも友人Bしたい場合AB両方がサードパーティにこのトークンをリークしていない場合は、他の 誰もA.foo()

を呼び出すことはできません、それは別のトークンタイプを必要とします。それが同じトークンタイプの場合、ABのタイプのトークンを持っているので、からA2のふりをすることができます。

チェックは実行時に行われ、コンパイル時ではなく完全ではありません。しかし、第三者がnullA.foo()を呼び出すだけなので、大したことではありません。コンパイル時に確認したい無罪のミスではありません。おそらく悪意のあるので、コンパイル時に呼び出し元に警告する気にはなりません。