簡體   English   中英

將參數化的類轉換為參數的子類

[英]Casting parameterized classes into subclasses of the parameter

有沒有辦法將某些超類型的列表轉換為子類型的列表? 似乎這應該可行,因為如果創建封裝通用列表的非通用類型,則可以按預期進行轉換。 例如:

class Foo {}
class Bar extends Foo {}

public class MyClass {
    public static void main(String[] args) {
        ArrayList<Foo> fooList = new ArrayList<>();
        fooList.add(new Bar());

        // No can do. "Cannot cast from ArrayList<Foo> to ArrayList<Bar>"
        // ArrayList<Bar> barList = (ArrayList<Bar>) fooList;
        // Bar bar = barList.get(0);

        FooList newAndImproveFooList = new FooList();
        newAndImproveFooList.add(new Bar());
        BarList barList = (BarList) newAndImproveFooList;
        barList.get(0);
    }
}

class FooList {
    private ArrayList<Foo> list = new ArrayList<>();

    // Simple delegates
    public void add(Foo foo) { list.add(foo); }
    public Foo get(int index) { return list.get(index); }
}

class BarList extends FooList {
    public Bar get(int index) { return (Bar) super.get(index); }
}

這段代碼的目的是表明可以“輕松”解決此強制轉換問題。 當然,我們可以轉換不同類型的數組(因此, FooList可以包含不是BarFoo的事實不應該成為問題。

看來,解決此問題的唯一方法是不使用泛型,但是您必須為需要列出列表的每種類型創建此列表的實例,這正是泛型要解決的問題解決。

我們可以對列表進行所有訪問,但是程序的當前布局(並且不能輕易更改)將導致數千次強制轉換,就好像我們可以對列表進行強制轉換一樣,我們只需要進行幾次強制轉換即可。在幾個地方。

而且我們無法復制該列表,因為它已在某些地方進行了修改。

最簡單的方法是做相當於

ArrayList<Bar> barList = (ArrayList<Bar>) (ArrayList<? extends Foo>) fooList;

這將引發大量有關未經檢查的強制轉換的編譯器警告。 編譯器正試圖警告您,這樣做沒有任何好處。 通過執行此barList ,您將丟失所有類型信息,並且如果barList的元素用作Bar對象, barList自己設置為ClassCastException

您可以將Bar項目添加到barList ,但由於它們是Foo對象,因此無法將其作為Bar對象檢索。

進行演員表是合法的,就像

Integer value = (Integer) (Object) "I'm not an Integer.";

但這並不意味着安全。 明智地使用它。

泛型的部分原因是您不進行強制轉換 如果正確使用了泛型,則可以靜態(足夠接近)確定涉及的所有類型。

在您的特定情況下,您不能將List<Foo>List<Bar>因為在轉換時或以后,它可能包含不是BarFoo 同樣,您不能將List<Bar> List<Foo>轉換為List<Foo> ,因為這樣可以添加不是BarFoo 類型參數與對象的當前狀態無關。 相反,它是對象類型的一個方面,除其他因素外,還影響所有可能的當前和將來狀態的空間。

您是正確的,可以通過使用裸露的類型而不是參數化的類型來完全避免類型安全性檢查。 如果參數化對您的影響超出其幫助范圍,那么也許這應該是您應該做的,但這只是代碼脆弱的標志(因為您的代碼依賴於無法靜態檢查的假設)。

而且,您可以通過裸露的中間類型在不兼容的參數化類型之間進行轉換來擊敗類型檢查(以警告為代價),但這絕不意味着您應該這樣做,也不意味着Java應該接受不兼容類型之間的直接轉換。

至於您的舊代碼,您可以使用類型界限和協變量返回類型至少取得一些進展:

abstract class Thing {
    public List<? extends Thing> getAgentList() {
        return Collections.<Thing>emptyList();
    }

    public List<? extends Thing> getHouseholdList() {
        return Collections.<Thing>emptyList();
    }
}

class Agent extends Thing {
    public List<Agent> getAgentList() {
        // ...
    }
}

class Household extends Thing {
    public List<Household> getHouseholdList() {
        // ...
    }
}

您描述的整個設計令人不舒服,因為超類具有暗示其子類知識的方法,即使它沒有實際的編譯時依賴性。 此外,對於Household ,使用方法getAgentList()似乎不太明智(例如)。 不過,只要您要這樣做,就不妨一路走下去:

abstract class Thing {
    public List<Agent> getAgentList() {
        return Collections.<Agent>emptyList();
    }

    public List<Household> getHouseholdList() {
        return Collections.<Household>emptyList();
    }
}

就個人而言,只要您正在編寫此代碼,我就會考慮利用這個機會將整個混亂重構為稍微復雜的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM