103

これは、候補者が Java 言語に追加されることを望んでいたものとして、最近のインタビューで私が尋ねた質問として出てきました。Java が具体化されたジェネリックを持っていないことは、一般的に苦痛として認識されていますが、プッシュされたとき、候補者は実際に私に、それらがあった場合に達成できたであろう種類のことを教えてくれませんでした。

明らかに、生の型は Java (および安全でないチェック) で許容されるため、ジェネリックを覆してList<Integer>、(たとえば) 実際にStrings を含む になる可能性があります。型情報が具体化されていれば、これは明らかに不可能になる可能性があります。しかし、これ以上のものがあるに違いありません

人々は本当にやりたいことの例を投稿できますか?具体化されたジェネリックが利用可能でしたか? つまり、実行時に a の型を取得できることは明らかですが、Listそれをどうするのでしょうか?

public <T> void foo(List<T> l) {
   if (l.getGenericType() == Integer.class) {
       //yeah baby! err, what now?

編集:回答は主にパラメータとして a を渡す必要性について懸念しているように見えるため、これを簡単に更新しClassます(たとえば、EnumSet.noneOf(TimeUnit.class))。これが不可能な線に沿って何かをもっと探していました。例えば:

List<?> l1 = api.gimmeAList();
List<?> l2 = api.gimmeAnotherList();

if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
    l1.addAll(l2); //why on earth would I be doing this anyway?
4

13 に答える 13

101

最も一般的に私を悩ませているのは、複数のジェネリック型にまたがる複数のディスパッチを利用できないことです。次のことは不可能であり、それが最善の解決策である場合が多くあります。

public void my_method(List<String> input) { ... }
public void my_method(List<Integer> input) { ... }
于 2009-12-18T12:35:06.347 に答える
81

この「必要性」に出くわした数回から、最終的には次の構成に要約されます。

public class Foo<T> {

    private T t;

    public Foo() {
        this.t = new T(); // Help?
    }

}

Tこれは、デフォルトのコンストラクターがあると仮定して、C# で機能します。でランタイム型typeof(T)を取得し、 でコンストラクタを取得することもできますType.GetConstructor()

Class<T>一般的な Java ソリューションは、 as 引数を渡すことです。

public class Foo<T> {

    private T t;

    public Foo(Class<T> cls) throws Exception {
        this.t = cls.newInstance();
    }

}

(必ずしもコンストラクター引数として渡す必要はありません。メソッド引数も問題ないため、上記は単なる例であり、try-catch簡潔にするために省略されています)

他のすべてのジェネリック型コンストラクトでは、実際の型はリフレクションの助けを借りて簡単に解決できます。以下の Q&A は、使用例と可能性を示しています。

于 2009-12-18T12:05:51.043 に答える
35

型安全が頭に浮かびます。パラメータ化された型へのダウンキャストは、具体化されたジェネリックなしでは常に安全ではありません。

List<String> myFriends = new ArrayList();
myFriends.add("Alice");
getSession().put("friends", myFriends);
// later, elsewhere
List<Friend> myFriends = (List<Friend>) getSession().get("friends");
myFriends.add(new Friend("Bob")); // works like a charm!
// and so...
List<String> myFriends = (List<String>) getSession().get("friends");
for (String friend : myFriends) print(friend); // ClassCastException, wtf!? 

また、抽象化のリークが少なくなります - 少なくとも、型パラメータに関する実行時情報に関心がある可能性がある抽象化です。現在、ジェネリック パラメーターの 1 つの型に関する実行時情報が必要な場合は、それも渡す必要がありますClass。このように、外部インターフェースは実装に依存します (パラメーターについて RTTI を使用するかどうか)。

于 2009-12-18T12:33:19.563 に答える
27

コードでジェネリック配列を作成できるようになります。

public <T> static void DoStuff() {
    T[] myArray = new T[42]; // No can do
}
于 2009-12-18T14:39:33.603 に答える
17

これは古い質問です。たくさんの回答がありますが、既存の回答は的外れだと思います。

「具体化」は単に本物を意味し、通常は型消去の反対を意味します。

Java Generics に関連する大きな問題:

  • この恐ろしいボクシング要件と、プリミティブと参照型の間の切断。これは具体化や型消去とは直接関係ありません。C#/Scala はこれを修正します。
  • セルフタイプはありません。このため、JavaFX 8 では「ビルダー」を削除する必要がありました。型消去とはまったく関係ありません。Scala はこれを修正しますが、C# については不明です。
  • 宣言側の型の差異はありません。C# 4.0/Scala にはこれがあります。型消去とはまったく関係ありません。
  • void method(List<A> l)とをオーバーロードできませんmethod(List<B> l)。これは型消去によるものですが、非常に些細なことです。
  • ランタイム タイプ リフレクションはサポートされていません。これが型消去の心臓部です。コンパイル時にプログラム ロジックの多くを検証および証明する超高度なコンパイラが好きな場合は、リフレクションをできるだけ使用しないようにする必要があります。このタイプの型消去は問題になりません。パッチが多く、スクリプトのような、動的な型プログラミングが好きで、コンパイラがロジックを可能な限り正しく証明することをあまり気にしない場合は、より良いリフレクションが必要であり、型の消去を修正することが重要です。
于 2014-03-26T16:55:42.267 に答える
14

シリアライゼーションは、具象化するとより簡単になります。私たちが望むのは

deserialize(thingy, List<Integer>.class);

私たちがしなければならないことは

deserialize(thing, new TypeReference<List<Integer>>(){});

見た目が悪く、ファンキーに機能します。

みたいなことを言ってくれると本当に助かる場合もあります。

public <T> void doThings(List<T> thingy) {
    if (T instanceof Q)
      doCrazyness();
  }

これらのものは頻繁に噛むことはありませんが、起こったときに噛む.

于 2014-08-06T23:07:37.613 に答える
11

私の Java Geneircs への露出は非常に限られており、他の回答が既に言及している点とは別に、具体化されたジェネリックが役立つ Maurice Naftalin と Philip Walder による書籍Java Generics and Collectionsで説明されているシナリオがあります。

型は具体化できないため、パラメーター化された例外を持つことはできません。

たとえば、以下のフォームの宣言は無効です。

class ParametericException<T> extends Exception // compile error

これは、スローされた例外が特定の型に一致するかどうかをcatch句がチェックするためです。このチェックは、インスタンス テストによって実行されるチェックと同じであり、型が具体化できないため、上記の形式のステートメントは無効です。

上記のコードが有効である場合、以下の方法での例外処理が可能でした。

try {
     throw new ParametericException<Integer>(42);
} catch (ParametericException<Integer> e) { // compile error
  ...
}

この本では、Java ジェネリックが C++ テンプレートの定義 (展開) と同様に定義されている場合、最適化の機会が増えるため、より効率的な実装につながる可能性があることにも言及しています。しかし、これ以上の説明はありませんので、知識のある方からの説明 (ポインタ) は役に立ちます。

于 2009-12-18T18:18:12.323 に答える
8

配列が具体化された場合、配列はおそらくジェネリックでよりうまく機能するでしょう。

于 2009-12-18T12:00:06.433 に答える
5

jdbc の結果セットを反復子として表示するラッパーがあります (つまり、依存関係の挿入により、データベースからの操作をより簡単に単体テストできます)。

API は次のようIterator<T>になります。T は、コンストラクターで文字列のみを使用して構築できる型です。Iterator は、SQL クエリから返された文字列を調べて、それを T 型のコンストラクターに一致させようとします。

ジェネリックが実装されている現在の方法では、結果セットから作成するオブジェクトのクラスも渡す必要があります。私が正しく理解していれば、ジェネリックが具体化されていれば、T.getClass() を呼び出すだけでそのコンストラクターを取得でき、Class.newInstance() の結果をキャストする必要がなくなります。

基本的に、オブジェクトからより多くのことを推測できるため、(単にアプリケーションを作成するのではなく) API を作成するのが簡単になると思います。一連の構成ではなく、Spring や xstream などで使用されているのを見ました。

于 2009-12-18T12:09:25.637 に答える
5

良いことの 1 つは、プリミティブ (値) 型のボックス化を回避することです。これは、他の人が提起した配列の苦情に多少関連しており、メモリの使用が制限されている場合、実際には大きな違いが生じる可能性があります。

パラメーター化された型を反映できることが重要なフレームワークを作成する場合、いくつかの種類の問題もあります。もちろん、これは実行時にクラス オブジェクトを渡すことで回避できますが、これにより API がわかりにくくなり、フレームワークのユーザーに追加の負担がかかります。

于 2009-12-18T20:51:52.713 に答える
3

特別なことを成し遂げるというわけではありません。理解するのが簡単になります。型消去は初心者にとっては苦労のように思えますが、最終的にはコンパイラの動作方法を理解する必要があります。

私の意見では、ジェネリックは、冗長なキャストを大幅に節約するための単なる追加機能です。

于 2009-12-18T12:02:33.467 に答える
0

今日私を捕らえたものは次のとおりです。具体化せずに、一般的なアイテムのvarargsリストを受け入れるメソッドを作成すると、呼び出し元はタイプセーフであると考えることができますが、誤って古いクラッドを渡して、メソッドを爆破します。

それが起こる可能性は低いと思われますか?...確かに、...データ型としてClassを使用するまで。この時点で、発信者は多くのClassオブジェクトを喜んで送信しますが、単純なタイプミスにより、Tに準拠していないClassオブジェクトが送信され、災害が発生します。

(注:ここで間違いを犯した可能性がありますが、「ジェネリックスvarargs」をグーグルで検索すると、上記はまさにあなたが期待しているように見えます。これを実際的な問題にしているのは、クラスの使用です。注意を怠る:()


たとえば、クラスオブジェクトをマップのキーとして使用するパラダイムを使用しています(単純なマップよりも複雑ですが、概念的にはそれが行われています)。

たとえば、これはJava Genericsでうまく機能します(簡単な例):

public <T extends Component> Set<UUID> getEntitiesPossessingComponent( Class<T> componentType)
    {
        // find the entities that are mapped (somehow) from that class. Very type-safe
    }

たとえば、Java Genericsで具体化することなく、これは任意の「クラス」オブジェクトを受け入れます。そして、それは前のコードのほんの小さな拡張です:

public <T extends Component> Set<UUID> getEntitiesPossessingComponents( Class<T>... componentType )
    {
        // find the entities that are mapped (somehow) to ALL of those classes
    }

上記の方法は、個々のプロジェクトで何千回も書き出す必要があるため、ヒューマンエラーの可能性が高くなります。間違いをデバッグすることは「面白くない」ことを証明しています。私は現在、代替案を見つけようとしていますが、あまり期待していません。

于 2011-10-16T14:34:38.523 に答える