ジェネリック医薬品の優れた点は何ですか?なぜジェネリック医薬品を使用するのでしょうか?

StackOverflow https://stackoverflow.com/questions/77632

  •  09-06-2019
  •  | 
  •  

質問

このソフトボールを公園から打ちたい人に差し上げようと思った。ジェネリックとは何ですか、ジェネリックの利点は何ですか、なぜ、どこで、どのように使用すればよいですか?かなり基本的な内容にしておいてください。ありがとう。

役に立ちましたか?

解決

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

他のヒント

私は同じことを繰り返すのが本当に嫌いです。私は必要以上に同じことを入力するのが嫌いです。私は、わずかな違いを含めて物事を何度も言い直すのが好きではありません。

作成する代わりに:

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

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

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

効率が 3 倍向上し、コピーを 1 つだけ維持するだけで済みます。保守するコードを減らしたくないのはなぜでしょうか?

これは、次のような非コレクション クラスにも当てはまります。 Callable<T> または Reference<T> 他のクラスと対話する必要があります。本当に延長しますか Callable<T> そして Future<T> および他のすべての関連クラスをタイプセーフなバージョンを作成するにはどうすればよいでしょうか?

私はしません。

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

しかし、あなたはそのことを十分に承知しているのではないかと思います。

ジェネリックを見るたびに、それは私に頭痛を与えます。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+空間

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

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

ジェネリックスの主な利点は、ジェネリックスが新しい Java 5 機能とうまく連携する方法にあります。 これは整数を Set そしてその合計を計算します。

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

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

このコード部分には、次の 3 つの新しい Java 5 機能が存在します。

まず、ジェネリックスとプリミティブのオートボックス化により、次の行が許可されます。

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

整数 10 にオートボックス化されます Integer の値で 10. 。(そして同じこと 42)。それからそれ Integer に投げ込まれます Set 保持することが知られている Integers.を投入しようとしています String コンパイルエラーが発生します。

次に、for-each ループはこれら 3 つすべてを受け取ります。

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

まず、 Set 含む Integerは 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 存在しているものです addセットに追加されました。

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

上記の行では、オートボクシングが行われています -- プリミティブ int 価値 10 そして 42 オートボックス化されています Integer に追加されるオブジェクト Set. 。ただし、覚えておいてください。 Integer オブジェクトは次のように扱われます Objects、コンパイラがその型を知るのに役立つ型情報がないためです。 Set 期待すべきだ。

for (Object o : set) {

これが重要な部分です。for-each ループが機能する理由は次のとおりです。 Set を実装します Iterable インターフェイス。 Iterator 存在する場合は型情報も付けます。(Iterator<T>, 、 あれは。)

ただし、型情報がないため、 Set を返します Iterator の値を返します。 Set として Objectそのため、要素は for-each ループで取得されます。 しなければならない タイプであること Object.

さて、 Object から取得されます Set, にキャストする必要があります。 Integer 手動で追加を実行するには:

  total += (Integer)o;

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

Integer 箱から出されて、 int そして、への加算を実行することを許可されます int 変数 total.

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

1.5 がリリースされる直前に Java バグ データベースを検索すると、7 倍以上のバグが見つかります。 NullPointerException よりも ClassCastException. 。したがって、バグを見つけるのに優れた機能であるとは思えません。少なくとも、少しのスモークテストを行った後でも残るバグを見つけることはできません。

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

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

Java のジェネリックにより容易になります パラメトリック多態性. 。型パラメータを使用して、引数を型に渡すことができます。ちょうど次のような方法として String foo(String s) 特定の文字列だけでなく、任意の文字列の動作をモデル化します。 s, 、つまり、次のようなタイプです List<T> 特定のタイプだけでなく、いくつかの動作をモデル化します。 どのタイプでも. List<T> そう言う どのタイプでも T, の種類があります。 List その要素は Ts. 。それで List は実際には 型コンストラクター. 。型を引数として受け取り、結果として別の型を構築します。

ここでは、私が毎日使用するジェネリック型の例をいくつか示します。まず、非常に便利な汎用インターフェイスです。

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

このインターフェイスは次のように述べています いくつかの 2 つのタイプについては、 A そして B, 、関数があります(と呼ばれます) f) がかかります A そして、を返します B. このインターフェースを実装すると、 A そして B 関数を提供する限り、任意の型を指定できます 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 つは、サブクラス化を回避できることです。サブクラス化すると、拡張が困難な脆弱なクラス階層が生じたり、階層全体を見ないと個別に理解するのが困難なクラスが生じたりする傾向があります。

ジェネリックの前には次のようなクラスがあったかもしれません Widget によって延長されました FooWidget, BarWidget, 、 そして BazWidget, 、ジェネリックを使用すると、単一のジェネリック クラスを持つことができます。 Widget<A> それは Foo, Bar または Baz コンストラクターで提供します Widget<Foo>, Widget<Bar>, 、 そして Widget<Baz>.

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

私がこれらを気に入っているのは、カスタム タイプを簡単に定義できるからです (とにかく私も使用しています)。

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

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

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

ジェネリックの最大のメリットはコードの再利用です。多数のビジネス オブジェクトがあり、各エンティティが同じアクションを実行するために非常に類似したコードを作成するとします。(つまり、Linq to 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 メソッドで異なる Type を指定すると、同じサービスが再利用されることに注目してください。まさにエレガント!

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

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

  • クラス作成時の一般的な型付け:

    パブリッククラスfoo <t> {public t get()...

  • キャストの回避 - 私はいつも次のようなことが嫌いでした

    new Comparator {public int Compareto(object o){if(o instanceof classicareabout)...

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

ジェネリック医薬品に対する私の最初の反応はあなたと同じで、「面倒すぎる、複雑すぎる」というものでした。私の経験では、これらを少し使用すると慣れてきて、それらを使用しないコードは明確に指定されていないように感じられ、快適性も低下します。それはさておき、Java 世界の残りの部分でもこれらが使用されているため、最終的にはそのプログラムを使用する必要があります。

良い例を挙げると。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();
}

メソッド/クラスの重要な概念がパラメータ/インスタンス変数の特定のデータ型に厳密に束縛されていないメソッド (またはクラス) を書いたことはありませんか (リンク リスト、最大/最小関数、二分探索などを考えてください) 、など)。

カットアンドペーストで再利用したり、強い型付けを損なうことなく、アルゴリズムやコードを再利用できたらと思ったことはありませんか (例:が欲しい List ではなく文字列の List 物事の私 希望 文字列です!)?

だからこそ、そうすべきです 欲しい ジェネリックス (またはそれより優れたもの) を使用します。

ジェネリックはクラスで使用されるだけでなく、メソッドでも使用できることを忘れないでください。たとえば、次のスニペットを考えてみましょう。

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 つの簡単な例にすぎません。ジェネリック メソッドを使用して実行できる優れた機能は他にもたくさんあります。私の意見では、最も優れているのは、ジェネリックを使用した型推論です。次の例を見てみましょう (Josh Bloch の『Effective Java 2nd Edition』から抜粋)。

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

これはあまり効果がありませんが、ジェネリック型が長い (またはネストされている) 場合に、多少の煩雑さを軽減します。つまり Map<String, List<String>>).

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

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

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

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

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

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

Java コレクションは使用します ジェネリック医薬品 Java 1.5以降。したがって、これらを使用するのに適した場所は、独自のコレクションのようなオブジェクトを作成するときです。

ほぼどこでも見かける例としては、Pair クラスがあります。これは 2 つのオブジェクトを保持しますが、それらのオブジェクトを汎用的な方法で処理する必要があります。

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

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

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

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

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

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

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

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

ジェネリックを使用すると、厳密に型指定されたオブジェクトを作成できますが、特定の型を定義する必要はありません。最も役立つ例は List および同様のクラスだと思います。

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

ジェネリックを使用すると、任意のオブジェクトを保持できるオブジェクトとデータ構造に対して強い型指定を使用できます。また、汎用構造 (ボックス化/アンボックス化) からオブジェクトを取得する際の、退屈でコストのかかる型キャストも排除します。

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

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

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

唯一の最大の理由は、彼らが提供するものです タイプセーフティ

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

とは対照的に、

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

簡単な例として。

要約すると、ジェネリックを使用すると、実行する内容をより正確に指定できます (より強力な型指定)。

これには、次のようないくつかの利点があります。

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

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

  • コンパイラーは、ボックス化の回避など、さらに多くの最適化を行うことができます。

(.NET の観点から言えば) 追加/拡張すべき点がいくつかあります。

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

メソッドのジェネリック引数でも同じことができますが、「Tell Don't Ask」原則をキャストに適用するのにも役立ちます。「私が望むものを与えてください。それができない場合は、その理由を教えてください。」

たとえば、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
}

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

私もスティーブやあなたと同じように、冒頭で言いました 「あまりにも乱雑で複雑すぎる」 しかし今ではその利点が分かりました

「型安全性」や「キャストなし」などの明白な利点についてはすでに述べたので、他の「利点」についてもお話しできるかもしれません。それが役立つことを願っています。

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

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

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

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

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

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

現在、MyObject 以外の誰も sth を設定できません。

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

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

これがすべて意味があることを願っています。

私はかつてこのテーマについて講演したことがあります。私のスライド、コード、音声録音は次の場所にあります。 http://www.adventuresinsoftware.com/generics/.

コレクションにジェネリックを使用するのはシンプルでクリーンです。たとえ他の場所でパントしたとしても、コレクションから得られる利益は私にとって勝利です。

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

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();
}

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

ジェネリックを使用すると、型固有のサポートを提供しながら、より再利用可能なオブジェクト/メソッドを作成できるようになります。場合によっては、パフォーマンスが大幅に向上することもあります。Java Generics の完全な仕様はわかりませんが、.NET では、 Interface、Constructor、Derivation の実装など、 Type パラメーターに制約を指定できます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top