[英]Preserving encapsulation of a generic in Java
晚上好。
我有一個相當復雜的問題。 為了練習Java,我一直在重新實現標准庫中的某些數據結構。 堆棧,LinkedList,樹等。通過一個非常簡單的示例,我剛剛確定了使用peek()
或pop()
方法時java.util.Stack
類將執行深層復制。 這是可以理解的,因為目標是保護類的內容不受外界干擾。 到目前為止,在我自己的Stack實現中(一個具有簡單數組的幼稚實現,稍后將提供鏈接列表),我一點也不在乎:
public class ArrayStack<T> implements Stack<T> {
private T[] data; // Will expand the array when stack is full.
private int top; // serves as both top and count indicator.
...
...
@Override
public T pop() throws EmptyStackException {
if(top == -1)
throw new EmptyStackException("Stack is empty.");
return data[top--]; // Shallow copy, dangerous!
}
不幸的是,由於無法實例化泛型,因此我無法假設有復制構造函數,並且無法執行諸如return new T(data[top--]);
我一直在尋找SO,並且找到了兩個相關的線程,它們試圖通過使用clone()
某些變體來解決問題。 該線程建議將該類的簽名擴展為:
public class ArrayStack<T extends DeepCloneableClass> implements Stack<T>
...
DeepCloneableClass
是一個類,該類實現允許“深度克隆”的接口(有關詳細信息,請參見該線程中的最高響應)。 當然,這種方法的問題是我真的不能期望標准類(例如String
或Integer
擴展我的自定義類,並且,當然,我所有現有的jUnit測試現在都在編譯時抱怨-時間,因為它們依賴於這樣的整數和字符串堆棧。 因此,我覺得這種解決方案似乎不可行。
該線程建議使用第三方庫克隆幾乎所有對象。 盡管該庫似乎仍受支持(最新的錯誤修復發生在不到一個月前),但我寧願不依賴第三方工具也不使用Java可以為我提供的一切。 原因是這些ADT的源代碼有一天可能會與大學生共享,而我寧願不要讓他們負擔安裝額外工具的負擔。
因此,我正在尋找一種簡單且可能的高效方法,以維護通用Java數據結構的內部完整性,同時仍允許與pop()
, peek()
, popFront()
等方法的簡單接口。
非常感謝您的幫助!
賈森
為什么需要克隆對象?
您的堆棧中只有一組引用。 您可能不需要克隆它們,只需創建一個新數組並在其中放入適當的引用,然后丟棄舊數組即可。
Integer
, Strings
等都是不可變的,因此從設計上講它們的內容是安全的。
至於自定義對象,盡管經驗豐富的Java程序員一定會對它有不同的感覺,但是實現自定義接口無疑是解決問題的一種方法。
另一個方法是使<T extends Serializable>
(由Integer
, String
等實現)並通過序列化“克隆” 。
但是,如果您想教給學生“正確的方式”,那么我肯定會使用第三方庫...您可以在項目中創建一個lib文件夾,並配置您的構建工具/ IDE,使用以下命令將所需的jar添加到Classpath中相對路徑,因此您的本科生無需安裝或設置任何東西。
僅供參考, 這個問題可能非常有用。
我一直在使用這種方法來講授Java入門課程(作為IT講師,而不是大學教授),它的痛苦程度遠沒有聽起來的那么大。
這些評論幫助我了解我的錯。 我使用以下示例向我和其他人“證明” Java標准庫的集合在提供對集合中對象的引用時會進行深層復制:
import java.util.Stack;
public class StackTestDeepCopy {
public static void main(String[] args){
Stack<String> st = new Stack<String>();
st.push("Jim");
st.push("Jill");
String top = st.peek();
top = "Jack";
System.out.println(st);
}
}
在打印st時,我看到對象沒有改變,並得出結論:已經進行了深拷貝。 錯誤! String
是不可變的,因此語句top = "Jack"
不會以任何方式修改String
(不是任何Object
都會被這樣的語句“修改”,但我並不是直覺),它只是使參考點指向堆上的新位置。 一個涉及實際可變類的新示例使我以自己的方式理解該錯誤。
現在已經解決了這個問題,我對標准庫允許這樣做的事實感到困惑。 為什么將標准庫中的訪問元素實現為淺表副本? 聽起來很不安全。
java.util.Stack
不會進行深層復制:
import java.util.Stack;
public class Test {
String foo;
public static void main(String[] args) {
Test test = new Test();
test.foo = "bar";
Stack<Test> stack = new Stack<Test>();
stack.push(test);
Test otherTest = stack.pop();
otherTest.foo = "wibble";
System.out.println("Are the same object: "+(test.foo == otherTest.foo));
}
}
結果是:
Are the same object: true
如果確實進行了復制,則test和otherTest將指向另一個對象。 典型的堆棧實現只是返回對添加到堆棧中的同一對象的引用,而不是副本。
您可能還想將數組項設置為null,然后再返回,否則數組仍將保留對該對象的引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.