ジェネリックスと型消去


10

Javaのジェネリックスは型消去を使用して実装されます。JLS は、インスピレーションは後方互換性であったと言います。一方、C#のジェネリックはreifiableです。

理論的には、ジェネリックスを「消去」または「復元可能」として持つことの利点と欠点は何ですか?

Javaは何かに欠けていますか?

回答:


8

まあ、動機(下位互換性)は利点と欠点の両方です。誰もがreifiableなタイプを好むので不利ですが、支払う代価は高かったです。C#での設計の選択を検討してください。それらはreifiable型を持っていますが、現在は重複するAPIを持っています。したがって、パラメーター化されたクラスごとにAPIが重複していたJava APIを想像してください。ここで、レガシークラスから新しいジェネリッククラスに数千行のコードを移植する様子を想像してください。さて、重複するAPIを不利に考えない人はいますか?しかし、ちょっと、彼らはreifiableタイプを持っています!

したがって、主な動機は「革命ではなく進化」でした。そして論理的には、すべての決定にはトレードオフがあります。

言及されている他の不利な点に加えて、特定の型が削除されることは明らかではないため、型の消去はコンパイル時に論理的に判断するのが難しい場合があるという事実を追加することもできます。

ブリッジメソッド(バイナリ互換性を維持するために構文的に生成されたこのコンパイラー)の存在も不利な点と見なされます。そして、これらは、前の段落で述べたエラーの理由の1つになり得ます。

主な欠点は、ジェネリック型には複数のクラスではなく単一のクラスがあるというすでに明白な事実に由来します。別の例として、同じジェネリッククラスを持つメソッドのオーバーロードがJavaで失敗することを考慮してください。

public void doSomething(List<One>);
public void doSomething(List<Two>);

reifiable型の欠点(少なくともC#では)と見なされる可能性があるのは、コードが爆発的に増加するという事実です。たとえば、List<int>あるクラスとa List<double>は、a List<string>とa であるため、まったく別のクラスList<MyType>です。したがって、クラスは実行時に定義する必要があり、クラスが爆発的に増加し、生成中に貴重なリソースが消費されます。

new T()別の回答で述べられているように、Javaでaを定義することができないという事実に関して、これが型消去の問題だけではないことを考慮することも興味深いです。また、デフォルトのコンストラクターが存在する必要があります。そのため、C#ではこれに「新しい制約」が必要です。(Javanew T()が不可能な理由、Alex Buckleyを参照)。


3
CLRでは、参照型 T sのClass<T>場合、すべてTのs に対してコードのコピーは1つしか得られないと私は思う。さらに、実際に使用される各値タイプの 追加のコピーT
AakashM 2012年

@AakashM:正しいです。ジェネリックごとに1つの参照タイプクラスがありますが、各値タイプは独自のバージョンを作成します。これは、タイプに基づいて特別なストレージスペースを提供するためです。すべての参照は同じストレージを使用しますが、値タイプはタイプごとに異なるストレージを使用します。
Guvante

6

タイプ消去の欠点は、実行時にジェネリックのタイプがわからないことです。これは、リフレクションを適用できず、実行時にインスタンス化できないことを意味します。

Javaでは次のようなことはできません。

public class MyClass<T> {
    private T t;

    //more methods    
    public void myMethod() {
        //more code
        if (someCondition) {
            t = new T();//illegal
        } else {
            T[] array = new T[];//illegal
        }            
    }       
}

これには回避策がありますが、より多くのコードが必要です。あなたがそれを述べたように、利点は後方互換性です。


3

消去されたジェネリックのもう1つの利点は、JVMにコンパイルされる言語が異なれば、ジェネリックに対して異なる戦略を採用することです(Scalaの定義サイトとJavaの使用サイトの共分散など)。また、Scalaの上位のジェネリックは、本質的に.Netの具体化された型でサポートするのがより困難でした。JVMでジェネリックを具体化した場合、それらの具体化されたジェネリックは、Scalaについて本当に気に入っている機能には適さない可能性が高く、次善の策で立ち往生することになります。引用オーラ・ビーニのブログ

つまり、具体化されたジェネリックをJVMに追加する場合、その実装は、独自のバージョンのジェネリックでイノベーションを実現したいすべての静的言語と、優れた実装とJavaライブラリとの優れたインターフェース機能を作成します。これらの基準を満たさない具体化されたジェネリックを追加すると、イノベーションが抑制され、JVMを多言語VMとして使用することがはるかに困難になるためです。

個人的には、TypeTagオーバーロードされたメソッドの競合にScalaでバインドされたコンテキストを使用する必要があることを不利とは考えていません。それほど頻繁ではない問題です。



消去されたジェネリックを支持するもう1つの理由は、式の問題の解決策を尊重する方法で依存関係を注入する必要があるためです。タイプの特定のケースをテストしたり、ジェネリック関数でファクトリをハードコーディングしたりしている場合、拡張性が間違っています。
シェルビームーアIII
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.