Java泛型简介–第6部分
這是對泛型的介紹性討論的延續, 此處的先前部分可以在此處找到。
在上一篇文章中,我們討論了關于類型參數的遞歸邊界。 我們看到了遞歸綁定如何幫助我們重用了車輛比較邏輯。 在該文章的結尾,我建議當我們不夠小心時,可能會發生類型混合。 今天我們將看到一個例子。
如果有人錯誤地通過以下方式創建了Vehicle的子類,則可能會發生混合:
枚舉不會發生這種類型的混淆,因為JVM負責子類化和為枚舉類型創建實例,但是如果我們在代碼中使用此樣式,則必須小心。
讓我們談談遞歸邊界的另一個有趣的應用。 考慮以下類別:
public class MyClass {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public MyClass() {}public String getAttrib1() {return attrib1;}public void setAttrib1(String attrib1) {this.attrib1 = attrib1;}public String getAttrib2() {return attrib2;}public void setAttrib2(String attrib2) {this.attrib2 = attrib2;}public String getAttrib3() {return attrib3;}public void setAttrib3(String attrib3) {this.attrib3 = attrib3;}public String getAttrib4() {return attrib4;}public void setAttrib4(String attrib4) {this.attrib4 = attrib4;}public String getAttrib5() {return attrib5;}public void setAttrib5(String attrib5) {this.attrib5 = attrib5;} }如果我們要創建此類的實例,則可以執行以下操作:
MyClass mc = new MyClass(); mc.setAttrib1("Attribute 1"); mc.setAttrib2("Attribute 2");上面的代碼創建該類的實例并初始化屬性。 如果我們可以在此處使用方法鏈接 ,那么我們可以編寫:
MyClass mc = new MyClass().setAttrib1("Attribute 1").setAttrib2("Attribute 2");顯然比第一個版本好得多。 但是,要啟用這種方法鏈接,我們需要通過以下方式修改MyClass :
public class MyClass {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public MyClass() {}public String getAttrib1() {return attrib1;}public MyClass setAttrib1(String attrib1) {this.attrib1 = attrib1;return this;}public String getAttrib2() {return attrib2;}public MyClass setAttrib2(String attrib2) {this.attrib2 = attrib2;return this;}public String getAttrib3() {return attrib3;}public MyClass setAttrib3(String attrib3) {this.attrib3 = attrib3;return this;}public String getAttrib4() {return attrib4;}public MyClass setAttrib4(String attrib4) {this.attrib4 = attrib4;return this;}public String getAttrib5() {return attrib5;}public MyClass setAttrib5(String attrib5) {this.attrib5 = attrib5;return this;} }然后我們將可以對此類的實例使用方法鏈接。 但是,如果我們想在涉及繼承的地方使用方法鏈接,那么事情就會變得混亂:
public abstract class Parent {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public Parent() {}public String getAttrib1() {return attrib1;}public Parent setAttrib1(String attrib1) {this.attrib1 = attrib1;return this;}public String getAttrib2() {return attrib2;}public Parent setAttrib2(String attrib2) {this.attrib2 = attrib2;return this;}public String getAttrib3() {return attrib3;}public Parent setAttrib3(String attrib3) {this.attrib3 = attrib3;return this;}public String getAttrib4() {return attrib4;}public Parent setAttrib4(String attrib4) {this.attrib4 = attrib4;return this;}public String getAttrib5() {return attrib5;}public Parent setAttrib5(String attrib5) {this.attrib5 = attrib5;return this;} }public class Child extends Parent {private String attrib6;private String attrib7;public Child() {}public String getAttrib6() {return attrib6;}public Child setAttrib6(String attrib6) {this.attrib6 = attrib6;return this;}public String getAttrib7() {return attrib7;}public Child setAttrib7(String attrib7) {this.attrib7 = attrib7;return this;} }/*** Now try using method chaining for instances of Child* in the following way, you will get compile time errors.*/ Child c = new Child().setAttrib1("Attribute 1").setAttrib6("Attribute 6");這樣做的原因是,即使Child從其父級繼承了所有的setter,所有這些setter方法的返回類型也都是Parent類型,而不是Child類型。 因此,第一個設置器將返回類型為Parent的引用,調用setAttrib6會導致編譯錯誤,因為它沒有任何此類方法。
我們可以通過在Parent上引入通用類型參數并在其上定義遞歸綁定來解決此問題。 它的所有子項從其擴展時都將自己作為類型參數傳遞,從而確保setter方法將返回其類型的引用:
public abstract class Parent<T extends Parent<T>> {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public Parent() {}public String getAttrib1() {return attrib1;}@SuppressWarnings("unchecked")public T setAttrib1(String attrib1) {this.attrib1 = attrib1;return (T) this;}public String getAttrib2() {return attrib2;}@SuppressWarnings("unchecked")public T setAttrib2(String attrib2) {this.attrib2 = attrib2;return (T) this;}public String getAttrib3() {return attrib3;}@SuppressWarnings("unchecked")public T setAttrib3(String attrib3) {this.attrib3 = attrib3;return (T) this;}public String getAttrib4() {return attrib4;}@SuppressWarnings("unchecked")public T setAttrib4(String attrib4) {this.attrib4 = attrib4;return (T) this;}public String getAttrib5() {return attrib5;}@SuppressWarnings("unchecked")public T setAttrib5(String attrib5) {this.attrib5 = attrib5;return (T) this;} }public class Child extends Parent<Child> {private String attrib6;private String attrib7;public String getAttrib6() {return attrib6;}public Child setAttrib6(String attrib6) {this.attrib6 = attrib6;return this;}public String getAttrib7() {return attrib7;}public Child setAttrib7(String attrib7) {this.attrib7 = attrib7;return this;} }請注意,我們已經明確地施放此為T類型,因為編譯器不知道這種轉換是否是可能的,即使它是因為牛逼的定義是由父<T>界。 同樣,由于我們將對象引用轉換為T ,因此編譯器將發出未經檢查的警告。 為了抑制這種情況,我們在設置器上方使用了@SuppressWarnings(“ unchecked”) 。
經過上述修改,這樣做是完全有效的:
Child c = new Child().setAttrib1("Attribute 1").setAttrib6("Attribute 6");當以這種方式編寫方法設置器時,我們應注意不要將遞歸邊界用于任何其他目的,例如從父級訪問子級狀態,因為這會使父級暴露其子類的內部細節,并最終破壞封裝。
通過這篇文章,我完成了泛型的基本介紹。 我在本系列中沒有討論太多的事情,因為我認為它們已經超出了介紹的范圍。
直到下一次。
翻譯自: https://www.javacodegeeks.com/2014/07/an-introduction-to-generics-in-java-part-6.html
總結
以上是生活随笔為你收集整理的Java泛型简介–第6部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux修改文件权限777(linux
- 下一篇: tcp长连接(长连接ddos)