2015-09-28 74 views
2

我想爲泛型添加對語言的支持,但在此之前,我需要更清楚地瞭解泛型的內部。將通用類型實現爲一種語言

我現在的理解是,實例化類:

class ArrayList<T> 
{ 
    public int add(T object) 
    { 
     // ... 
    } 
} 

創建一個實例,其中的Tadd參數的類型必須是一樣的new ArrayList<T>()類型參數,其中T是一個真正的類型。

實現這看起來很簡單,但僅限於此用例。當它需要支持自省時,它變成一個更復雜的概念。例如,我不會說:

true === (new ArrayList<Date>() instanceof ArrayList) 

,但我要說的是:

true === (new ArrayList<Date>() instanceof ArrayList<Date>) 

我的問題是,使用ArrayList<Date>作爲一種參考的時候,是它派生的類型ArrayList<T>,或者是在自己的權利的一類,例如是:

$list = new ArrayList<Date>(); 

的一個實例:

class ArrayList 
{ 
    public int add(Date $object) 
    { 
     // ... 
    } 
} 

或者是它的ArrayList<T>其中TDate一個實例?

+0

'ArrayList '只在類聲明中使用,如果這是你的意思。使用'ArrayList '作爲類型是沒有意義的(如果'T'未知)。 – Hacketo

+0

@Hacketo我知道,我的意思是抽象的。我試圖找出實例化對象是否仍然擁有它是一個泛型類型的實例的信息,或者是否使用展開的類型參數創建了一種新類型的虛擬類,這個對象現在是一個實例。這個問題對我來說非常重要,因爲它會決定整個語言中類型參數的傳播位置。如果它們從實例化中傳播出來,那麼只需要引用這些類型,如果從類名稱中,類需要從聲明中複製(比喻)。 – Flosculus

+0

我的評論是關於'新的ArrayList ()instanceof ArrayList ',當T不知道時不能成立,如果'T'是'Date',那麼你的結果與'new ArrayList ()instanceof ArrayList '。 'T''只用於泛型類的聲明,就像一個變量持有一個類型。 – Hacketo

回答

1

Java泛型和C++模板看起來相似,但以相反的方式實現。正如Steffen Kreutz在評論中所說的那樣,java爲它的泛型使用了類型擦除。類型擦除意味着在編譯時,java控制對泛型類的訪問,但是在運行時,所有類型信息都消失了,並且ArrayList<Date>ArrayList<Integer>分享完全是相同的代碼。

這與模板有很大不同。在模板中,每個具體類都是在編譯時實現的,因此(在C++中)vector<double>vector<char>是兩個不同的類,它們的編譯代碼將會不同,因爲一個會採用雙參數,另一個會採用字符而它們不會傳遞在堆棧中以相同的方式。

如果你需要一個泛型類來知道它可以接受的類型,你必須明確地使用一個屬性來保存它。例如:

class MyGen<T> { 
    class<T> myClazz; 

    MyGen(class<T> clazz) { 
     myClass = clazz; 
    } 
    ... 
} 

然後,您可以使用myClazz反射,因爲它是一個真正的類對象在運行時可用,而T是唯一在編譯的時候可用,無法通過反射來使用。

+0

我真的不想把這個問題與Java關聯起來,我計劃實現的泛型類型更接近於.NET。我正在試驗的語言是解釋的PHP。所以每個對象都需要攜帶一個聲明類的「變體」。類型擦除是不可能的,因爲鑄造對象在PHP中不存在,因爲它是動態的。不過,通過存儲變體(類似於你的屬性例子)或者通過展開類,就可以進行反省,就像在C++中一樣。問題是,哪個更好?我應該提到這種語言是動態的。 – Flosculus

+0

對不起,如果我有點混亂,你的答案確實解決了我的兩個選擇。我只是想要考慮創建每個「具體類」的性能影響與維護跨語言「屬性」的難度。 – Flosculus

+0

@Flosculus:我認爲你應該爲PHP添加標籤,如果它是你正在試驗的語言。不幸的是,我真的不知道如何使用PHP。但是,對於也是動態語言的Python,我會嘗試使用自定義註釋來傳遞類型信息,也可能使用抽象基類來存儲它。 –

0

泛型只是編譯時的助手。如果我們沒有仿製藥,我們會寫這樣的代碼:

ArrayList list = new ArrayList(); 
list.add(new Date()); 
Date date = (Date) list.get(0); 

仿製藥已被引入除去必要的強制類型轉換,現在我們可以這樣寫:

ArrayList<Date> list = new ArrayList<>(); 
list.add(new Date()); 
Date date = list.get(0); 

你可能會認爲ArrayList類管理內部有Date陣列,但事實上 它管理着一排Object s。編譯器會在您訪問元素的位置插入缺失的強制轉換。

使用泛型還有另一個好處。編譯器可以檢測你是否進行無效轉換。所以這是不可能寫出這樣的事:

ArrayList<Date> list = new ArrayList<>(); 
list.add(new Date()); 
String date = list.get(0); 

您可以驗證ArrayList s的不同類型的參數仍然編譯使用new ArrayList<String>().getClass().equals(new ArrayList<Date>().getClass())同一類。請參考Oracle Java Documentation about type erasure

相關問題