2013-01-09 67 views
5

如何實現Java中的朋友概念(如C++)?在Java中實現Friend概念

+0

即使我用C++編寫代碼,如果有的話,我也會非常謹慎地使用'friend'。提供一個可供所有人使用的公共接口更安全。 –

+0

我的代碼中沒有使用Java中的朋友概念。好奇心,我只是好奇。 –

+0

@ Code-Guru公共界面如何提供幫助?我可能會在這裏錯過一些東西。你可以發佈一些鏈接給出更多的細節? –

回答

23

Java沒有C++中的friend關鍵字。然而,有一種方法可以效仿它;這種方式實際上給了更多更精確的控制。假設您有類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++好友關鍵字更笨拙,但卻更加細緻。類A可以精確選擇哪些方法公開哪些類。

現在,在上述情況下,A允許訪問B的所有實例和B的子類實例。如果不希望後者,則giveKeyTo()方法可以在內部使用getClass檢查其他類型的實際類型( ),並且拋出異常(如果它不是精確的B)。

+0

非常好。實現這一點的更好方法可能是將'B'類聲明爲'final',從而避免潛在的安全風險。 – user991710

1

在Java中,您可以將兩個(或更多)類放入同一個包中。包含protected限定符的所有方法和字段可以直接由該包中的所有類訪問。

+0

事實上,如果現有的遺留代碼已經存在並且重構將會很麻煩並且您不想改變字段和方法的可見性,那麼設置這樣的朋友功能可能是一種選擇。我同意,它不會被高度閱讀或甚至不推薦,但它可以作爲一個快速解決方案。 –

+0

我覺得有必要指出這是最後一個漏洞,出於選擇,最壞的情況,歡迎進入次級水平的技術債務,當它成爲永久性暫時或暫時的永久性選擇時。 –

+0

幾乎所有我認識的人都會將受影響的方法定義爲公開的,但您的方法很有趣 – 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可以生成一個非空B.ToA令牌。如果兩個AB不要將此令牌泄漏到第三方, 沒有其他人可以調用A.foo()

如果A2希望朋友B過,它需要一個不同的令牌類型。如果它是相同的標記類型,由於A得到B類型的標記,因此A可以假裝爲BA2

檢查是在運行時完成的,而不是編譯時間,這是不完美的。雖然沒什麼大不了,因爲任何第三方只能調用A.foo()null,它不能是我們想在編譯時檢查的無辜錯誤;它可能是惡意的,所以我們不在乎在編譯時警告調用者。