ジェネリックの何がクールなのですか、なぜそれらを使用するのですか?


87

私はこのソフトボールを公園から打ちたい人に提供したいと思いました。ジェネリックとは何ですか、ジェネリックの利点は何ですか、なぜ、どこで、どのように使用する必要がありますか?かなり基本的なものにしてください。ありがとう。


1
stackoverflow.com/questions/77632/…の複製。しかし、簡単な答えです。コレクションがAPIの一部でなくても、内部実装でも不要なキャストは好きではありません。
Matthew Flaschen 2009年

質問は非常に似ていますが、受け入れられた答えがこれに答えるとは思いません。
トムホーティン-タックライン2009年


4
@MatthewFlaschen奇妙な、あなたの複製はこの質問に向けられています...マトリックスの不具合!
jcollum 2012年

@jcollum、コメントを投稿した元の質問がこの質問にマージされたと思います。
Matthew Flaschen 2012年

回答:


126
  • コードを記述したり、タイプセーフなライブラリメソッドを使用したりできます。つまり、List <string>は文字列のリストであることが保証されています。
  • ジェネリックスが使用されている結果として、コンパイラーは型の安全性についてコードに対してコンパイル時チェックを実行できます。つまり、文字列のリストにintを入れようとしていますか?ArrayListを使用すると、透過性の低いランタイムエラーになります。
  • ボックス化/ボックス化解除(.netが値型を参照型に変換する必要がある場合またはその逆)を回避するか、オブジェクトから必要な参照型にキャストするため、オブジェクトを使用するよりも高速です。
  • 同じ基本的な動作を持つ多くのタイプに適用できるコードを記述できます。つまり、Dictionary <string、int>はDictionary <DateTime、double>と同じ基本的なコードを使用します。ジェネリックスを使用すると、フレームワークチームは、前述の利点を備えた両方の結果を達成するために1つのコードを記述するだけで済みました。

9
ああ、とてもいいです、そして私は弾丸リストが好きです。これは、これまでで最も完全でありながら簡潔な答えだと思います。
MrBoJangles 2008

説明全体は「コレクション」のコンテキストにあります。私はより広い外観を探しています。何かご意見は?
jungle_mole 2015年

良い答え私は他に2つの利点を追加したいだけです、ジェネリック制限には1-のほかに2つの利点があります-ジェネリック型で制約付き型のプロパティを使用できます(たとえば、T:IComparableがCompareToを使用するために提供する場合)2-書く人何が
起こる

52

私は自分自身を繰り返すのが本当に嫌いです。同じことを必要以上に頻繁に入力するのは嫌いです。少しの違いはあるものの、何度も言い直すのは好きではありません。

作成する代わりに:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

再利用可能なクラスを1つ作成できます...(何らかの理由でrawコレクションを使用したくない場合)

class MyList<T> {
   T get(int index) { ... }
}

私は今では3倍効率が良く、1つのコピーを維持するだけで済みます。なぜあなたはより少ないコードを維持したくないのですか?

これは、他のクラスと相互作用するCallable<T>必要がReference<T>あるaやaなどの非コレクションクラスにも当てはまります。本当に拡張Callable<T>Future<T>て、他のすべての関連クラスを拡張して 、タイプセーフなバージョンを作成しますか?

私はしません。


1
私は上手く理解できていない気がします。「MyObject」ラッパーを作成することはお勧めしません。それは恐ろしいことです。オブジェクトのコレクションが車のコレクションである場合は、ガレージオブジェクトを作成することをお勧めします。get(car)はなく、buyFuel(100)のように、100ドルの燃料を購入して、それを最も必要とする車に分配するメソッドがあります。私は「ラッパー」だけでなく、実際のビジネスクラスについて話している。たとえば、コレクションの外でそれらを取得したりループしたりすることはほとんどありません。オブジェクトにそのメンバーを要求するのではなく、代わりに操作を実行するように要求します。
ビルK

ガレージ内でも、型安全性が必要です。つまり、List <Cars>、ListOfCarsがあるか、どこにでもキャストできます。必要最小限の回数以上、タイプを述べる必要はないと思います。
James Schek 2009年

型安全性が良いと私は同意しますが、ガレージクラスに限定されているため、あまり役に立ちません。カプセル化されており、簡単に制御できるので、新しい構文を犠牲にして、この小さな利点が得られるというのが私のポイントです。それは、信号無視を違法にするために米国憲法を修正するようなものです-はい、それは常に違法であるべきですが、それは改正を正当化しますか?また、実際には比較的有害なこのケースではカプセル化を使用しないように人々を促していると思います。
ビルK

ビル-カプセル化を使用しないことについてのポイントがあるかもしれませんが、残りの議論は不十分な比較です。この場合、新しい構文を学習するコストは最小限です(これをC#のラムダと比較してください)。これを憲法の改正と比較しても意味がありません。憲法には、交通法典を指定する意図はありません。羊皮紙に手書きでコピーするのではなく、ポスターボードにセリフフォントの印刷版に切り替えるようなものです。それは同じ意味ですが、はるかに明確で読みやすくなっています。
James Schek 2009年

この例は、概念をより実用的にします。それをありがとう。
RayLoveless 2018年

22

タイプキャストする必要がないことは、コンパイル時に型チェックを実行するため、Javaジェネリックの最大の利点の1つです。これにより、ClassCastException実行時にスローされる可能性が低くなり、より堅牢なコードにつながる可能性があります。

しかし、あなたはそれを完全に知っていると思います。

Genericsを見るたびに、頭痛の種になります。Javaの最良の部分は、その単純さと最小限の構文であり、ジェネリックは単純ではなく、かなりの量の新しい構文を追加することだと思います。

最初は、ジェネリック医薬品のメリットもわかりませんでした。私は1.4構文からJavaを学び始めました(当時Java 5はリリースされていましたが)、ジェネリックスに遭遇したとき、書くコードが多いと感じ、その利点を本当に理解していませんでした。

最新のIDEを使用すると、ジェネリックスを使用したコードの記述が簡単になります。

最新のまともなIDEのほとんどは、ジェネリックスを使用したコードの記述、特にコード補完を支援するのに十分なほどスマートです。

ここで作るの例だMap<String, Integer>とはHashMap。入力する必要のあるコードは次のとおりです。

Map<String, Integer> m = new HashMap<String, Integer>();

そして確かに、それは新しいを作るためだけにタイプすることがたくさんありHashMapます。ただし、実際には、Eclipseが必要なものを認識する前に、これだけ入力する必要がありました。

Map<String, Integer> m = new Ha Ctrl+Space

確かに、HashMap候補のリストから選択する必要がありましたが、基本的にIDEは、汎用タイプを含め、何を追加するかを知っていました。適切なツールがあれば、ジェネリックを使用することはそれほど悪くありません。

さらに、型がわかっているため、汎用コレクションから要素を取得すると、IDEは、そのオブジェクトがすでに宣言された型のオブジェクトであるかのように動作します。IDEがオブジェクトの型を知るためにキャストする必要はありません。です。

ジェネリックスの主な利点は、Java5の新機能とうまく連携する方法にあります。整数をaに投げ込みSet、その合計を計算する例を次に示します。

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

そのコードには、次の3つの新しいJava5機能があります。

まず、プリミティブのジェネリックスとオートボクシングにより、次の行が可能になります。

set.add(10);
set.add(42);

整数10Integer、値が10。のに自動ボックス化されます。(そして42)についても同じです。次にIntegerSetそれはIntegersを保持することが知られているに投げ込まれます。をスローしようとStringすると、コンパイルエラーが発生します。

次に、for-eachループは次の3つすべてを取ります。

for (int i : set) {
  total += i;
}

まず、Set含まれているIntegersがfor-eachループで使用されます。各要素はであると宣言さintれており、Integerがプリミティブにボックス化されていないため、これが許可されintます。そして、このアンボクシングがジェネリックがあったことを指定するために使用されたため知られて発生しているという事実Integerで開催されたのSet

ジェネリックスは、Java 5で導入された新機能をまとめる接着剤になることができ、コーディングをより簡単かつ安全にするだけです。また、ほとんどの場合、IDEは適切な提案を行うのに十分なほどスマートであるため、一般的に、タイピングがそれほど多くなることはありません。

そして率直に言って、Set例からわかるように、Java 5の機能を利用すると、コードがより簡潔で堅牢になると思います。

編集-ジェネリックスのない例

以下は、Setジェネリックを使用しない上記の例の図です。それは可能ですが、必ずしも快適ではありません。

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(注:上記のコードは、コンパイル時に未チェックの変換警告を生成します。)

非ジェネリックコレクションを使用する場合、コレクションに入力される型は型のオブジェクトですObject。したがって、この例でObjectは、aがaddセットに組み込まれているものです。

set.add(10);
set.add(42);

上記の行では、オートボクシングが機能しています。つまり、プリミティブint値で10あり、オブジェクト42にオートボックス化されており、Integerオブジェクトはに追加されていSetます。ただし、コンパイラがどの型を予期すべきかを知るのに役立つ型情報がないため、IntegerオブジェクトはObjectsとして処理されていることにSet注意してください。

for (Object o : set) {

これは重要な部分です。for-eachループが機能する理由はSetIterableインターフェイスが実装されているためIteratorです。インターフェイスは、存在する場合はwith型の情報を返します。(Iterator<T>、つまり。)

ただし、型情報がないため、はasの値Setを返すanIteratorを返します。そのため、for-eachループで取得される要素は型である必要があります。SetObjectObject

今すぐことObjectから取得されSet、それはにキャストする必要がInteger加算を実行するために、手動で:

  total += (Integer)o;

ここでは、型キャストから行われるObjectまでInteger。この場合、これは常に機能することがわかっていますが、手動の型キャストでは、他の場所で小さな変更を加えると破損する可能性がある脆弱なコードであると常に感じます。(私はすべてのタイプキャストがClassCastException起こるのを待っていると感じます、しかし私は逸脱します...)

これで、Integerはボックスから解放さintれ、int変数への加算を実行できるようになりますtotal

Java 5の新機能が非ジェネリックコードで使用できることを説明できればと思いますが、ジェネリックでコードを書くほどクリーンで単純ではありません。そして、私の意見では、Java 5の新機能を最大限に活用するには、ジェネリックスを調べる必要があります。少なくとも、コンパイル時のチェックで、無効なタイプキャストが実行時に例外をスローするのを防ぐことができます。


拡張されたforループとの統合は本当に素晴らしいです(非ジェネリックコレクションではまったく同じ構文を使用できないため、いまいましいループは壊れていると思いますが、intにはcast / autoboxを使用する必要があります。必要なすべての情報!)、しかしあなたは正しいです、IDEのヘルプはおそらく良いでしょう。追加の構文を正当化するのに十分かどうかはまだわかりませんが、少なくともそれは私が恩恵を受けることができるものです(これは私の質問に答えます)。
ビルK

@Bill K:ジェネリックを使用せずにJava5機能を使用する例を追加しました。
coobird 2009年

それは良い点です、私はそれを使用していませんでした(私は1.4で、そして何年も前から立ち往生していると言いました)。私は利点があることに同意しますが、私が到達している結論は、A)OOを正しく使用していない人には非常に有利であり、使用している人にはあまり役に立たない傾向があるということです。B)あなたの利点記載されている利点は利点ですが、Javaで実装されているジェネリックスを実際に開発するために必要な大きな変更は言うまでもなく、小さな構文の変更を正当化するのに十分な価値はありません。しかし、少なくとも利点があります。
ビルK

15

あなたは1.5がリリースされたばかりの前のJavaのバグデータベースを検索した場合、あなたはと7倍以上のバグを見つけるだろうNullPointerExceptionよりをClassCastException。したがって、バグ、または少なくとも少しのスモークテスト後も存続するバグを見つけることは素晴らしい機能ではないようです。

私にとってジェネリックの大きな利点は、重要な型情報をコードで文書化することです。その型情報をコードで文書化したくない場合は、動的に型付けされた言語、または少なくともより暗黙的な型推論を備えた言語を使用します。

オブジェクトのコレクションをそれ自体に保持することは悪いスタイルではありません(ただし、一般的なスタイルはカプセル化を効果的に無視することです)。それはむしろあなたがしていることに依存します。コレクションを「アルゴリズム」に渡すことは、ジェネリックスを使用して(コンパイル時またはその前に)チェックするのが少し簡単です。


6
それは文書化されているだけでなく(それ自体が大きな勝利です)、コンパイラーによって強制されます。
James Schek 2009年

良い点ですが、「このタイプのオブジェクトのコレクション」を定義するクラスにコレクションを含めることによっても達成されませんか?
ビルK

1
James:はい、それがコードで文書化さている点です
トムホーティン-タックライン2009年

「アルゴリズム」にコレクションを使用する優れたライブラリを私は知らないと思います(おそらく、コレクションのオブジェクトのメソッドであるべきだと私に信じさせる「関数」について話していることを意味します)。JDKは、データのコピーであるすてきでクリーンな配列をほぼ常に抽出して、混乱しないようにしていると思います。少なくとも、ほとんどの場合はそうです。
ビルK

また、コレクションがどのように使用されるかを正確に記述し、それを制限するオブジェクトが、はるかに優れた形式のコード内ドキュメントの使用を制限していませんか?ジェネリックスで与えられた情報は、良いコードでは非常に冗長だと思います。
ビルK

11

Javaのジェネリックスは、パラメトリック多相性を促進します。型パラメーターを使用して、型に引数を渡すことができます。のようなメソッドString foo(String s)が特定の文字列だけでなく任意の文字列sに対してList<T>何らかの動作をモデル化するのと同じように、そのようなタイプは特定のタイプだけでなく任意のタイプに対していくつかの動作をモデル化しますどのタイプにも、要素がsあるタイプがあるList<T>と言います。だから、実際には型コンストラクタが。引数として型を取り、結果として別の型を構築します。TListTList

これが私が毎日使っているジェネリック型の例です。まず、非常に便利な汎用インターフェース:

public interface F<A, B> {
  public B f(A a);
}

このインターフェースは、いくつかの2つのタイプ、AおよびBに対して、fを取り、Aを返す関数(と呼ばれる)があることを示していますBあなたは、このインタフェースを実装し、ときAB限り、あなたは機能提供として、あなたが望む任意の種類のことができf、前者を取り、後者を返します。インターフェイスの実装例は次のとおりです。

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

ジェネリックス以前は、キーワードを使用してサブクラス化することでポリモーフィズムが実現されていましたextends。ジェネリックスを使用すると、実際にはサブクラス化を廃止し、代わりにパラメトリック多相を使用できます。たとえば、任意のタイプのハッシュコードを計算するために使用されるパラメーター化された(ジェネリック)クラスについて考えてみます。Object.hashCode()をオーバーライドする代わりに、次のようなジェネリッククラスを使用します。

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

これは、継承を使用するよりもはるかに柔軟性があります。これは、脆弱な階層を固定することなく、合成とパラメトリック多態性を使用するというテーマにとどまることができるためです。

ただし、Javaのジェネリックは完全ではありません。たとえば、型を抽象化することはできますが、型コンストラクターを抽象化することはできません。つまり、「任意の型Tに対して」と言うことはできますが、「型パラメーターAをとる任意の型Tに対して」と言うことはできません。

Javaジェネリックのこれらの制限についての記事をここに書きました。

ジェネリックスの大きな利点の1つは、サブクラス化を回避できることです。サブクラス化は、拡張が難しいクラス階層を脆弱にし、階層全体を見ずに個別に理解するのが難しいクラスになる傾向があります。

Wereasは、あなたがクラスを持っているかもしれないジェネリック医薬品の前に好きなWidgetだけ延長FooWidgetBarWidgetおよびBazWidget、ジェネリックを使用すると、単一の一般的なクラスができWidget<A>取りFooBarまたはBazそのコンストラクタであなたを与えるためにWidget<Foo>Widget<Bar>Widget<Baz>


Javaにインターフェースがない場合は、サブクラス化についての良い点になります。FooWidget、BarWidtet、BazWidgetのすべてにウィジェットを実装させるのは正しいのではないでしょうか。私はこれについて間違っているかもしれませんが..しかし、私が間違っている場合、これは本当に良い点です。
ビルK

ハッシュ値を計算するために動的なジェネリッククラスが必要なのはなぜですか?ジョブに適切なパラメータを持つ静的関数をより効率的にしませんか?
Ant_222 2018年

8

ジェネリックは、ボクシングとボックス化解除のパフォーマンスへの影響を回避します。基本的に、ArrayListとList <T>を比較してください。どちらも同じコア機能を実行しますが、オブジェクトとの間でボックス化する必要がないため、List <T>の方がはるかに高速です。


それが本質ですよね?いいね。
MrBoJangles 2008

完全ではありません。これらは、型の安全性に役立ちます。また、ボックス化/ボックス化解除をまったく行わずに、参照型のキャストを回避するのにも役立ちます。
ljs 2008

したがって、次の質問は、「なぜArrayListを使用したいのか」ということだと思います。そして、答えを危険にさらすと、ArrayListsは、値をボックス化およびボックス化解除することをあまり期待しない限り、問題ないと思います。コメント?
MrBoJangles 2008

いいえ、.net2の時点では機能していません。下位互換性のためにのみ役立ちます。ArrayListは低速で、タイプセーフではなく、ボックス化/ボックス化解除またはキャストのいずれかが必要です。
ljs 2008

値の型(intやstructなど)を格納していない場合、ArrayListを使用するときにボックス化は行われません。クラスはすでに「オブジェクト」です。安全性のタイプのため、List <T>を使用する方がはるかに優れています。本当にオブジェクトを格納する場合は、List <object>を使用する方が適しています。
Wilka 2008

6

Genericsの最大の利点は、コードの再利用です。多数のビジネスオブジェクトがあり、同じアクションを実行するために各エンティティに対して非常に類似したコードを作成するとします。(IE LinqからSQLへの操作)。

ジェネリックスを使用すると、特定の基本クラスから継承する任意の型を指定して操作できるクラスを作成したり、次のように特定のインターフェイスを実装したりできます。

public interface IEntity
{

}

public class Employee : IEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

public class Company : IEntity
{
    public string Name { get; set; }
    public string TaxID { get; set }
}

public class DataService<ENTITY, DATACONTEXT>
    where ENTITY : class, IEntity, new()
    where DATACONTEXT : DataContext, new()
{

    public void Create(List<ENTITY> entities)
    {
        using (DATACONTEXT db = new DATACONTEXT())
        {
            Table<ENTITY> table = db.GetTable<ENTITY>();

            foreach (ENTITY entity in entities)
                table.InsertOnSubmit (entity);

            db.SubmitChanges();
        }
    }
}

public class MyTest
{
    public void DoSomething()
    {
        var dataService = new DataService<Employee, MyDataContext>();
        dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
        var otherDataService = new DataService<Company, MyDataContext>();
            otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });

    }
}

上記のDoSomethingメソッドでタイプが異なる場合、同じサービスが再利用されることに注意してください。本当にエレガント!

あなたの仕事にジェネリックを使用する理由は他にもたくさんありますが、これが私のお気に入りです。


5

カスタムタイプをすばやく定義する方法を提供するので、私はそれらが好きです(とにかくそれらを使用します)。

したがって、たとえば、文字列と整数で構成される構造を定義し、それらの構造の配列などにアクセスする方法についてオブジェクトとメソッドのセット全体を実装する必要がある代わりに、辞書を作成できます。

Dictionary<int, string> dictionary = new Dictionary<int, string>();

そして、コンパイラー/ IDEが残りの面倒な作業を行います。特に辞書では、最初のタイプをキーとして使用できます(繰り返し値はありません)。


5
  • 型付きコレクション-使用したくない場合でも、他のライブラリや他のソースからのコレクションを処理する必要があります。

  • クラス作成におけるジェネリックタイピング:

    public class Foo <T> {public T get()..。

  • キャスティングの回避-私はいつも次のようなものが嫌いでした

    new Comparator {public int compareTo(Object o){if(o instanceof classIcareAbout).. ..

インターフェースがオブジェクトで表現されているためにのみ存在するはずの条件を本質的にチェックしている場合。

ジェネリックに対する私の最初の反応はあなたの反応と似ていました-「あまりにも厄介で、あまりにも複雑」。私の経験では、それらを少し使用した後は慣れてしまい、それらのないコードは明確に指定されていないように感じられ、快適さも低下します。それとは別に、Javaの世界の残りの部分はそれらを使用しているので、最終的にはプログラムを使用する必要がありますよね?


1
キャストの回避に関して正確に言うと、Sunのドキュメントによると、キャストは発生しますが、コンパイラによって実行されます。コードにキャストを記述しないようにするだけです。
デミ

おそらく、私は1.4を超えることを望まない企業の長い列に閉じ込められているように見えるので、誰が知っているか、私は5に達する前に引退するかもしれません。私は最近、「SimpleJava」をフォークすることは興味深い概念かもしれないと考えています。 --Javaは機能を追加し続ける予定であり、私が見た多くのコードにとって、それは健全なことではありません。私はすでにループをコーディングできない人々に対処しています-彼らが無意味に展開されたループにジェネリックを注入しようとするのを見たくありません。
ビルK

@Bill K:ジェネリックの全力を使う必要のある人はほとんどいません。これは、それ自体が汎用のクラスを作成している場合にのみ必要です。
エディ

5

良い例を挙げましょう。Fooというクラスがあると想像してみてください

public class Foo
{
   public string Bar() { return "Bar"; }
}

例1 ここで、Fooオブジェクトのコレクションが必要です。LIstまたはArrayListの2つのオプションがあり、どちらも同じように機能します。

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

上記のコードで、Fooの代わりにFireTruckのクラスを追加しようとすると、ArrayListがそれを追加しますが、Fooの汎用リストによって例外がスローされます。

例2。

これで2つの配列リストができました。それぞれでBar()関数を呼び出します。ArrayListはオブジェクトで埋められているため、barを呼び出す前にオブジェクトをキャストする必要があります。ただし、Fooの汎用リストにはFooのみを含めることができるため、それらに対して直接Bar()を呼び出すことができます。

foreach(object o in al)
{
    Foo f = (Foo)o;
    f.Bar();
}

foreach(Foo f in fl)
{
   f.Bar();
}

質問を読む前にあなたが答えたと思います。これらは、私にはあまり役に立たない2つのケースです(問題で説明されています)。それが何か役に立つことをする他のケースはありますか?
ビルK

4

メソッド/クラスの主要な概念がパラメーター/インスタンス変数の特定のデータ型(リンクリスト、最大/最小関数、バイナリ検索など)に厳密にバインドされていないメソッド(またはクラス)を作成したことはありませんか?など)。

カットアンドペーストの再利用や強い型付けを損なうことなく、アルゴリズム/コードを再利用できることを望んでいませんか(たとえばList、文字列が欲しいのですが、文字列が欲しいのでListありません!)。

あなたがすべき理由の希望は、ジェネリック(か何かより良い)を使用します。


この種のものをコピーして貼り付ける必要はありませんでした(どのように発生するかわかりませんか?)ニーズに合ったインターフェイスを実装し、そのインターフェイスを使用します。これができないまれなケースは、純粋なユーティリティ(コレクションなど)を作成している場合であり、それらの場合(質問で説明したように)、実際には問題にはなりません。私は強い型付けを妥協します-反射を使用する場合と同じように。強いタイピングができないので、リフレクションの使用を絶対に拒否しますか?(リフレクションを使用する必要がある場合は、コレクションのようにカプセル化します)。
ビルK

3

ジェネリックスはクラスだけでなく、メソッドでも使用できることを忘れないでください。たとえば、次のスニペットを取り上げます。

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

シンプルですが、とてもエレガントに使えます。良い点は、メソッドが与えられたものを何でも返すことです。これは、呼び出し元に再スローする必要がある例外を処理するときに役立ちます。

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

重要なのは、メソッドを通過させても型が失われないということです。Throwableジェネリックなしで実行できるのは、だけではなく、正しいタイプの例外をスローできます。

これは、ジェネリックメソッドの1つの使用法の簡単な例です。ジェネリックメソッドでできることは他にもたくさんあります。私の意見では、最もクールなのはジェネリックスによる型推論です。次の例を見てください(JoshBlochのEffectiveJava 2nd Editionから抜粋)。

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

これはあまり効果がありませんが、ジェネリック型が長い(またはネストされている、つまりMap<String, List<String>>)場合は、混乱を減らすことができます。


例外についてはそうは思わない。それは私に考えさせますが、ジェネリックスをまったく使用しなくても(そしてより明確なコードで)まったく同じ結果が得られると95%確信しています。型情報はオブジェクトの一部であり、キャスト先ではありません。私はそれを試してみたいと思っています-多分私は明日仕事であなたに戻ってきます..何を教えてください、私はそれを試してみるつもりですそしてあなたが正しいなら私は通過するつもりです投稿のリストと5つの投稿に投票してください:)そうでない場合は、ジェネリックスが単純に利用できるため、コードが複雑になり、メリットがないことを考慮してください。
ビルK

これにより、さらに複雑になるのは事実です。しかし、私はそれがいくつかの用途を持っていると思います。問題はThrowable、特定の宣言された例外があるメソッド本体内からプレーンオールドをスローできないことです。別の方法は、各例外タイプを返す個別のメソッドを作成するか、を返す非ジェネリックメソッドを使用して自分でキャストを行うことThrowableです。前者は冗長すぎてまったく役に立たず、後者はコンパイラから何の助けも得られません。ジェネリックスを使用することにより、コンパイラーは自動的に正しいキャストを挿入します。だから、問題は:それは複雑にする価値がありますか?
ジガウォット2009年

わかりました。だからキャストが出てくるのを避けます...良い点です。私はあなたに借りて5
ビル・K

2

ミッチェルが指摘するように、主な利点は、複数のクラスを定義する必要がない強い型付けです。

このようにして、次のようなことができます。

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

ジェネリックスがなければ、その関数にアクセスするには、blah [0]を正しい型にキャストする必要があります。


選択肢があれば、キャストするよりキャストしたくない。
MrBoJangles 2008

強い型付けは、特にコンパイル時の型チェックが可能であることを考えると、ジェネリックスimhoの最良の側面です。
ljs 2008

2

jvmはとにかくキャストします...ジェネリック型を「オブジェクト」として扱うコードを暗黙的に作成し、目的のインスタンス化へのキャストを作成します。Javaジェネリックは単なる構文糖衣です。


2

これがC#の質問であることは知っていますが、ジェネリックは他の言語でも使用されており、その使用/目標は非常に似ています。

JavaコレクションはJava1.5以降のジェネリックを使用します。したがって、それらを使用するのに適した場所は、独自のコレクションのようなオブジェクトを作成する場合です。

ほとんどどこでも見られる例は、2つのオブジェクトを保持するPairクラスですが、これらのオブジェクトを一般的な方法で処理する必要があります。

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

このPairクラスを使用するときはいつでも、処理するオブジェクトの種類を指定でき、型キャストの問題は実行時ではなくコンパイル時に表示されます。

ジェネリックスは、キーワード「super」および「extends」で境界を定義することもできます。たとえば、ジェネリック型を処理したいが、Foo(setTitleメソッドを持つ)というクラスを拡張することを確認したい場合は、次のようにします。

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

それ自体はあまり興味深いものではありませんが、FooManagerを扱うときはいつでも、それがMyClass型を処理すること、およびMyClassがFooを拡張することを知っていると便利です。


2

Sun Javaのドキュメントから、「なぜジェネリックを使用する必要があるのですか?」

「ジェネリックスは、コレクションのタイプをコンパイラーに伝達してチェックできるようにする方法を提供します。コンパイラーがコレクションの要素タイプを認識すると、コンパイラーは、コレクションが一貫して使用されていることをチェックし、挿入できるようになります。コレクションから取り出される値の正しいキャスト...ジェネリックスを使用するコードはより明確で安全です....コンパイラーはコンパイル時に型制約が実行時に違反されていないことを確認できます[強調鉱山]。プログラムは警告なしにコンパイルされるので、実行時にClassCastExceptionがスローされないことを確実に述べることができます。特に大規模なプログラムでジェネリックを使用することの正味の効果は、読みやすさと堅牢性の向上です。[強調鉱山]」


コレクションのジェネリック型付けが私のコードに役立つケースは見たことがありません。私のコレクションは厳密にスコープされています。だから私はこれを「助けてくれませんか?」と言いました。副次的な質問として、ジェネリックを使い始める前に、これが時々起こったと言っていますか?(コレクションに間違ったオブジェクトタイプを配置する)。本当の問題のない解決策のように思えますが、運が良かったのかもしれません。
ビルK

@Bill K:コードが厳密に管理されている(つまり、自分だけが使用している)場合、おそらくこれが問題になることはないでしょう。それは素晴らしいことです!最大のリスクは、複数の人が取り組む大規模なプロジェクト、IMOにあります。これはセーフティネットであり、「ベストプラクティス」です。
デミ

@Bill K:これが例です。ArrayListを返すメソッドがあるとします。ArrayListがジェネリックコレクションではないとしましょう。誰かのコードがこのArrayListを受け取り、そのアイテムに対して何らかの操作を実行しようとします。唯一の問題は、ArrayListにBillTypesを入力し、コンシューマーがConsumerTypesを操作しようとしていることです。これはコンパイルされますが、実行時に爆発します。ジェネリックコレクションを使用すると、代わりにコンパイラエラーが発生します。これは、処理がはるかに優れています。
デミ

@Bill K:私はさまざまなスキルレベルの人々と協力してきました。プロジェクトを誰と共有するかを選択する余裕がありません。したがって、型安全性は私にとって非常に重要です。はい、ジェネリックスを使用するようにコードを変換することでバグを見つけました(そして修正しました)。
エディ

1

ジェネリックスを使用すると、強く型付けされたオブジェクトを作成できますが、特定の型を定義する必要はありません。最も便利な例は、Listと同様のクラスだと思います。

汎用リストを使用すると、リストリストリストを好きなように作成でき、強い型付けをいつでも参照できます。配列や標準リストのように変換する必要はありません。


1

ジェネリックスを使用すると、任意のオブジェクトを保持できるはずのオブジェクトとデータ構造に強い型を使用できます。また、一般的な構造(ボックス化/ボックス化解除)からオブジェクトを取得するときに、面倒で費用のかかるタイプキャストを排除します。

両方を使用する1つの例は、リンクリストです。リンクリストクラスがオブジェクトFooしか使用できない場合、どのようなメリットがありますか?あらゆる種類のオブジェクトを処理できるリンクリストを実装するには、リストに1つのタイプのオブジェクトのみを含める場合は、リンクリストと仮想ノード内部クラスのノードをジェネリックにする必要があります。


1

コレクションに値型が含まれている場合、コレクションに挿入するときにオブジェクトをボックス化/ボックス化解除する必要がないため、パフォーマンスが大幅に向上します。resharperのようなクールなアドオンは、foreachループのように、より多くのコードを生成できます。


1

ジェネリックス(特にコレクション/リスト)を使用するもう1つの利点は、コンパイル時の型チェックを利用できることです。これは、オブジェクトのリストの代わりに汎用リストを使用する場合に非常に便利です。


1

最も理由は、型安全性を提供することです。

List<Customer> custCollection = new List<Customer>;

とは対照的に、

object[] custCollection = new object[] { cust1, cust2 };

簡単な例として。


型安全性、それ自体の議論。多分誰かがそれについて質問するべきです。
MrBoJangles 2008

ええ、でもあなたは何をほのめかしていますか?型安全性の要点は?
Vin

1

要約すると、ジェネリックスを使用すると、意図することをより正確に指定できます(より強力なタイピング)。

これにはいくつかの利点があります。

  • コンパイラはあなたが何をしたいのかをもっと知っているので、型が互換性があることをすでに知っているので、多くの型キャストを省略できます。

  • これにより、プログラムの正しさに関する以前のフィードバックも得られます。以前は実行時に失敗していたもの(たとえば、オブジェクトを目的のタイプにキャストできなかったため)は、コンパイル時に失敗するようになり、テスト部門が不可解なバグレポートを提出する前に間違いを修正できます。

  • コンパイラーは、ボクシングの回避など、より多くの最適化を行うことができます。


1

追加/拡張するいくつかの事柄(.NETの観点から言えば):

ジェネリック型を使用すると、ロールベースのクラスとインターフェイスを作成できます。これはすでにより基本的な用語で言われていますが、型にとらわれない方法で実装されたクラスを使用してコードを設計し始めると思います。その結果、コードの再利用性が高くなります。

メソッドに関する一般的な議論は同じことを行うことができますが、キャストに「Tell Do n't Ask」の原則を適用するのにも役立ちます。つまり、「私が欲しいものを教えてください。できない場合は、理由を教えてください」。


1

たとえば、SpringORMとHibernateで実装されたGenericDaoでこれらを使用します。

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

ジェネリックを使用することにより、このDAOの実装は、GenericDaoをサブクラス化するだけで、開発者に設計されたエンティティのみを渡すように強制します。

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

私の小さなフレームワークはより堅牢です(フィルタリング、遅延読み込み、検索などがあります)。例を示すために、ここで簡略化しました

私はスティーブやあなたと同じように、最初は「面倒で複雑すぎる」と言っていましたが、今ではその利点がわかります


1

「型安全性」や「鋳造なし」などの明らかなメリットについてはすでに説明しているので、他の「メリット」についてお話ししたいと思います。

まず第一に、ジェネリックは言語に依存しない概念であり、IMOは、通常の(実行時の)ポリモーフィズムを同時に考えるとより理にかなっているかもしれません。

たとえば、オブジェクト指向設計からわかるように、ポリモーフィズムには実行時の概念があり、プログラムの実行が進むにつれて呼び出し元オブジェクトが実行時に把握され、実行時のタイプに応じて関連するメソッドが呼び出されます。ジェネリックスでは、考え方は多少似ていますが、すべてがコンパイル時に行われます。それはどういう意味ですか、そしてそれをどのように利用しますか?

(コンパクトに保つ​​ためにジェネリックメソッドに固執しましょう)これは、(以前にポリモーフィッククラスで行ったように)別々のクラスで同じメソッドを使用できることを意味しますが、今回はコンパイラーによって自動生成され、設定されたタイプによって異なりますコンパイル時。コンパイル時に指定した型でメソッドをパラメーター化します。したがって、実行時のポリモーフィズム(メソッドのオーバーライド)で行うように、すべての型に対してメソッドを最初から作成する代わりに、コンパイル中にコンパイラーに作業を行わせることができます。システムで使用される可能性のあるすべてのタイプを推測する必要がないため、コードを変更せずにはるかにスケーラブルになるため、これには明らかな利点があります。

クラスはほとんど同じように機能します。タイプをパラメーター化すると、コードはコンパイラーによって生成されます。

「コンパイル時」の概念を理解したら、「制限付き」型を使用して、クラス/メソッドを介してパラメーター化された型として渡すことができるものを制限できます。したがって、通過するものを制御できます。これは、特に他の人がフレームワークを消費している場合に強力です。

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

現在、MyObject以外のsthを設定することはできません。

また、メソッド引数に型制約を「適用」することができます。つまり、両方のメソッド引数が同じ型に依存することを確認できます。

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

このすべてが理にかなっていることを願っています。


0

私はかつてこのトピックについて話をしました。私のスライド、コード、およびオーディオ録音は、http: //www.adventuresinsoftware.com/generics/で見つけることができます


0

コレクションにジェネリックを使用するのは簡単でクリーンです。あなたが他のどこでもそれをパントしたとしても、コレクションからの利益は私にとっての勝利です。

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

vs

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

または

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

それだけでもジェネリックの限界「コスト」の価値があり、これを使用して価値を得るのにジェネリックの第一人者である必要はありません。


1
ジェネリックスがなくても、次のことができます。ListstuffList = getStuff(); for(Object stuff:stuffList){((Stuff)stuff).doStuff(); }、これはそれほど違いはありません。
トムホーティン-タックライン2009年

ジェネリックスがなければ、stuffList.doAll();を実行します。私はいつも、まさにそのようなコードの場所であるラッパークラスを持っていると言いました。それ以外の場合、このループを他の場所にコピーしないようにするにはどうすればよいですか?再利用のためにどこに置きますか?ラッパークラスにはおそらく何らかのループがありますが、そのクラスはすべて「もの」に関するものであるため、トムが上記で提案したようなキャストされたforループを使用することは、かなり明確で自己文書化されています。
ビルK

@ジェリコ、それは本当に面白いです。あなたがそのように感じる理由があるのだろうか、そしてそれが私が理解していないことであるのだろうか?それを避けるために、はるかに複雑な構文を追加します)。なぜ「何かをキャストしなければならないのなら、あなたはすでに負けている」のですか?
ビルK

@Jherico:Sunのドキュメントによると、ジェネリックスはコンパイラーにオブジェクトのキャスト先に関する知識を提供します。コンパイラーはユーザーではなくジェネリックスのキャストを行うので、これはステートメントを変更しますか?
デミ

コードをJava1.5より前のジェネリックスに変換しましたが、その過程で、間違ったオブジェクトタイプがコレクションに入れられるバグが見つかりました。また、手動でキャストする必要がある場合、ClassCastExceptionのリスクが発生するため、「失ったキャストをキャストする必要がある場合」のキャンプに分類されます。ジェネリック医薬品では、コンパイラはあなたのために目に見えないそれをしないが、ClassCastExceptionが発生する可能性がある方法型の安全性のために減少しました。はい、ジェネリックスがなくてもObjectを使用できますが、それは「例外をスローする」と同じように、私にはひどいコードの臭いです。
エディ

0

ジェネリックスは、タイプ固有のサポートを提供しながら、より再利用可能なオブジェクト/メソッドを作成する機能も提供します。また、場合によっては多くのパフォーマンスが得られます。Java Genericsの完全な仕様はわかりませんが、.NETでは、Implements a Interface、Constructor、DerivationなどのTypeパラメーターに制約を指定できます。


0
  1. プログラマーがジェネリックアルゴリズムを実装できるようにする-ジェネリックを使用することにより、プログラマーは、さまざまなタイプのコレクションで機能し、カスタマイズでき、タイプセーフで読みやすいジェネリックアルゴリズムを実装できます。

  2. コンパイル時のより強力な型チェック-Javaコンパイラは、ジェネリックコードに強力な型チェックを適用し、コードが型の安全性に違反している場合にエラーを発行します。コンパイル時エラーの修正は、見つけるのが難しいランタイムエラーの修正よりも簡単です。

  3. 円柱の除去。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.