2015-08-23 129 views
31

我來自C++背景,目前正在學習Java。當我嘗試使用一些第三方庫時出現了一個問題。我如何確定對以對象引用作爲參數的方法的調用是否會修改對象? 在C++中,由於使用了const關鍵字,因此很明顯。如果方法簽名是:如何確定Java方法是否修改作爲參數傳遞的對象

void foo(Boo& boo); 

我知道被引用的對象可能會被修改,而如果該方法的簽名是:

void foo(const Boo& boo); 

的編譯器保證引用的對象沒有被修改。

我還沒有在Java中看到類似的東西,因爲只有引用本身可以被聲明爲final,而不是被引用的對象,並且最終的參數首先沒有什麼意義,因爲它仍然被值傳遞。因此,當我看到的方法,例如:

void foo(Boo boo) {...} 

如何確定由BOO引用的對象的功能的體內修飾(也許使用註釋)?如果沒有辦法知道,是否有一些廣泛使用的約定或一些最佳實踐,以避免混淆和錯誤?

+4

閱讀文檔。 (您可能認爲這是Java相對於C++的一個弱點,但我們不會錯過它,而不是錯過使用C++工作時檢查異常的情況。) – user2357112

+2

最好的做法是在可能的情況下避免突變。這適用於所有事情,而不僅僅是方法的參數。 – usr

+0

@ user2357112「*您在使用C++時錯過檢查異常」*使用異常來控制程序流是一種錯誤,異常是(如其名稱所述)針對**異常情況**。具有語言特徵,其唯一目的是能夠使用例外作爲通信通道從錯誤中恢復,這只是「流量控制的例外」的另一種情況,如上述規則的設計錯誤所述。我們不會錯過檢查的異常。 – Manu343726

回答

13

不幸的是,這裏沒有這樣的設施。一個好的防禦做法是將你傳遞的數據對象定義爲不可變的(即,沒有任何允許修改其狀態的公共方法)。如果你是真的關心這個,你可以在將對象傳遞給你不信任的方法之前複製/克隆一個對象,但這通常是一種多餘的預防措施。

24

我該如何確定boo引用的對象是否在函數體內部修改過?(可能使用註釋)?

不幸的是只能讀取代碼。

如果沒有辦法知道,是否有一些廣泛使用的約定或一些最佳實踐,以避免混淆和錯誤?

公共約定是傳遞無法修改的對象,如果需要使用包裝器。這確保類不能修改對象。

List<String> readOnly = Collections.unmodifiableList(list); 

如果對象是可複製的,你也可以使用clone()但另一種常見的方法是使用一個副本。

List<String> readOnly = new ArrayList<>(list); 

如果你關心這樣的行爲,單元測試可以顯示一個方法是否修改一個對象。如果你已經有單元測試,通常需要一到兩行來檢查這個。

+1

作爲從C++進行遷移的人的建議,提及您不應該在Java中使用'clone',因爲[idiom已損壞](http://www.artima.com/intv/bloch13。 HTML)。 –

+0

@MickMnemonic如果在實現中存在錯誤,則應該避免調用clone()。如果該對象還不是Cloneable,則使其成爲Cloneable對於複雜對象尤其難以處理,並且應避免使用非平凡對象。我已經看到了一些錯誤,如果首先使用'clone()'來避免它,並用一些更多的bug替換它也不是答案。 –

+2

'clone'只在極少數情況下有用,其中正確的實現已經存在。在新代碼中,沒有理由暴露'克隆',甚至沒有微不足道的類;只需提供一個拷貝構造函數。 –

-3

有一種方法,即方法開發人員應該將參數標記爲final,如果它不打算修改參數。

 public void test(final Object param) 

然而很少有人關注這個,所以很難知道。然而好的程序員遵循這個規則,特別是編寫api。如果你想寫方法並且暴露它。使參數最終表明傳遞的對象不會被修改。

+7

這樣做是一個壞主意,因爲它沒有做它看起來做的事情。它可以防止在方法內部修改* reference *參數,但是由於這是通過值傳遞的,因此調用者將無法看到您是否修改了它。它不會阻止修改'param'對象。 –

+0

這甚至顯示在Javadoc?或通過反思?我不確定有沒有人知道你是否這樣做。 – user2357112

+1

@Peter,這實際上是一個_推薦的做法,不是一個壞的做法,因爲它向任何人表明你不應該重新分配參考。你是對的,這不應該與不變性混淆。更多討論[在這個線程](http://stackoverflow.com/questions/316352/why-would-one-mark-local-variables-and-method-parameters-as-final-in-java)。 –

6

副作用分析沒有內置到Java語言中。

您可以通過手動檢查執行副作用分析,但有多種工具可以使過程自動化。

您可以使用推理工具(123)來檢測代碼是否對參數產生副作用。

您也可以在代碼中編寫純度或副作用註釋,然後使用檢查/驗證工具(12)確保您的代碼符合您所寫的註釋。

所有上述鏈接的工具都有侷限性,但您可能會發現它們很有用。如果您知道其他工具,請在評論中提及它們。

8

注:這個答案是

You can also write purity or side-effect annotations in your code一個更詳細的版本 - mernst

存在着中它可以在編譯時通過註解檢查各種事物的Checker FrameworkIJG Immutablity checker。此檢查器允許您使用@Immutable@ReadOnly註釋對象引用。

問題是你經常會自己去annotate the library。爲了簡化您的任務,Checker框架可以將automatically infer部分註釋;你仍然必須自己做很多事情。

+1

「@ Immutable」註解強制對象不可變。原始的海報詢問了參考不變性,它由'@ ReadOnly'註釋提供。 – mernst

+2

關於[Checker Framework](http://checkerframework.org/)的狀態,Google每天都會在數百個項目中使用它。它也用於華爾街和許多其他從業者和學者。這不是官方的標準,也沒有任何關聯的JSR - 它只是一個有用的工具。 – mernst

3

如何判斷boo引用的對象是否在 函數的主體(可能使用註釋)中被修改?

我必須與其他答案同意,沒有直接的方法來確定該方法將修改對象或不和是確保方法不能修改Object,你都必須做的是從你身邊。

如果沒有辦法知道,有沒有一些廣泛使用的約定或 一些最佳實踐,以避免混淆和錯誤?

這裏方法名來到現場。繼續使用方法的命名約定,我們必須看看一些方法聲明,這些方法聲明清楚地說服你,你的Object根本不會被改變。

例如,你知道,Arrays.copyOf不會改變你的實際的數組,System.out.println(boo)不會改變你的boo

方法名是真實武器提供給用戶的方法是儘可能詳細的信息。 (是的,這總是不可能的,但要遵循一個很好的做法。)

讓我們考慮它在你的情況說printBoo只會打印,copyBoo只會複製,clearBoo將重置所有屬性,checkAndCreateNewBoo將檢查您的booObject,如果需要創建新的。

因此,最終如果我們能夠以正確的方式使用它們,調用方可以保證調用方法後Object保持不變。

2

正如大家說的,更喜歡使用不可變對象,也避免void的方法

的方法可用的目的是這樣

void foo(Boo boo) {...} 

被改變物體本身的狀態或更改對象作爲參數傳遞

void completOrder(Order order) { ... } 
//or 
void parserTokenEnded(String str) { ... } 
相關問題