CodeGym /Java Blog /ランダム /Java のジェネリックス
John Squirrels
レベル 41
San Francisco

Java のジェネリックス

ランダム グループに公開済み
やあ!Java ジェネリックについて説明します。たくさんのことを学べると言わざるを得ません。このレッスンだけでなく、今後のいくつかのレッスンでもジェネリックについて取り上げます。したがって、ジェネリック医薬品に興味があるなら、今日は幸運な日です。ジェネリック医薬品の特徴について多くを学ぶことができます。そうでない場合は、諦めてリラックスしてください。:) これは非常に重要なトピックなので、知っておく必要があります。まずは単純な「何を」と「なぜ」から始めましょう。

Java ジェネリックとは何ですか?

ジェネリックはパラメーターを持つ型です。ジェネリック型を作成するときは、型だけでなく、それが動作するデータ型も指定します。最も明白な例はすでに頭の中に浮かんでいると思います: ArrayList! 通常、プログラム内でこれを作成する方法は次のとおりです。

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
ご想像のとおり、このリストの特徴は、すべてをこのリストに詰め込むことができないことです。このリストはStringオブジェクトに対してのみ機能します。ここで、Java の歴史に少し脱線して、「なぜ?」という質問に答えてみましょう。これを行うには、独自の簡略版の ArrayList クラスを作成します。私たちのリストは、内部配列にデータを追加したり、内部配列からデータを取得したりする方法のみを知っています。

public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
リストにIntegerのみを格納したいとします。ジェネリック型は使用していません。add()メソッドに明示的な「instanceof Integer 」チェックを含めたくありません。そうした場合、クラス全体はIntegerにのみ適しており、世界中の他のすべてのデータ型に対して同様のクラスを作成する必要があります。私たちはプログラマーに頼り、コードにコメントを残して、望まないものが追加されないようにします。

// Use this class ONLY with the Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
プログラマーの 1 人がこのコメントを見逃し、うっかり数値のリストにいくつかの文字列を入れて、それらの合計を計算してしまいました。

public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
コンソール出力:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
      at Main.main (Main.java:14)
この状況の最悪の部分は何ですか? 確かにプログラマーの不注意ではありません。最悪の部分は、間違ったコードがプログラム内の重要な場所に配置され、正常にコンパイルされてしまうことです。ここで、コードの作成中にバグが発生するのではなく、テスト中にのみ発生します (これが最良のシナリオです!)。開発の後半段階でバグを修正すると、金銭的にも時間的にもはるかに多くのコストがかかります。これはまさにジェネリックの利点です。ジェネリック クラスを使用すると、運が悪いプログラマでもエラーをすぐに検出できます。プログラムはコンパイルできません。

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();
      
       myList1.add(100);
       myList1.add(100);
       myList1.add ("Lolkek"); // Error!
       myList1.add("Shalala"); // Error!
   }
}
プログラマーは自分の間違いにすぐに気づき、すぐに改善します。ちなみに、この種のエラーを確認するために独自のListクラスを作成する必要はありませんでした。山かっこを削除して、通常の ArrayList から ( <Integer> ) を入力するだけです。

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
コンソール出力:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
     at Main.main(Main.java:16)
言い換えれば、Java の「ネイティブ」メカニズムを使用していても、この種の間違いを犯して安全でないコレクションを作成する可能性があります。ただし、このコードを IDE に貼り付けると、「java.util.List の生の型のメンバーとしての add(E) への呼び出しがチェックされていません」という警告が表示されます。項目を追加するときに問題が発生する可能性があると言われます。ジェネリック型のないコレクションに。しかし、「生の型」という言葉は何を意味するのでしょうか? raw型は、型が削除されたジェネリック クラスです。つまり、List myList1 は生の型です。raw 型の反対はジェネリック型、つまりパラメータ化された型を示すジェネリック クラスです。たとえば、List<String> myList1なぜこの言語でraw 型の使用が許可されているのかと疑問に思われるかもしれません。理由は簡単です。Java の作成者は、互換性の問題の発生を避けるために、Java でのraw 型のサポートを残しました。Java 5.0 がリリースされるまでに (ジェネリックはこのバージョンで初めて登場しました)、多くのコードはすでにraw 型を使用して記述されていました。その結果、このメカニズムは現在でもサポートされています。私たちはレッスンで Joshua Bloch の古典的な本「Effective Java」について繰り返し言及しました。この言語の作成者の 1 人として、彼は著書の中で 生の型ジェネリック型を省略しませんでした。Javaのジェネリックとは何ですか?  - 2この本の第 23 章には、「新しいコードでは生の型を使用しないでください」という非常に雄弁なタイトルが付いています。これは覚えておく必要があることです。ジェネリック クラスを使用する場合は、ジェネリック型をraw 型に変換しないでください。

一般的なメソッド

Java では、いわゆるジェネリック メソッドを作成することで、個々のメソッドをパラメータ化できます。このような方法はどのように役立ちますか? 何よりも、さまざまな種類のメソッド パラメーターを操作できるという点で役立ちます。同じロジックを異なる型に安全に適用できる場合は、汎用メソッドが優れたソリューションとなる可能性があります。これは非常に単純な例だと考えてください。 myList1というリストがあるとします。リストからすべての値を削除し、すべての空のスペースを新しい値で埋めたいと考えています。ジェネリック メソッドを使用したクラスは次のようになります。

public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Old String 1");
       strings.add("Old String 2");
       strings.add("Old String 3");

       fill(strings, "New String");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
構文に注意してください。少し変わったように見えます:

public static <T> void fill(List<T> list, T val)
戻り値の型の前に <T> を書きます。これは、汎用メソッドを扱っていることを示しています。この場合、メソッドは入力として 2 つのパラメーター (T オブジェクトのリストと別の別の T オブジェクト) を受け入れます。<T> を使用することで、メソッドのパラメータ タイプをパラメータ化します。文字列と整数のリストを渡すことはできません。String のリストと String、Integer のリストと Integer、独自のCatオブジェクトのリストと別のCatオブジェクト、これが私たちが行う必要があることです。main ()メソッドは、fill()メソッドを使用してさまざまな種類のデータを簡単に操作できる方法を示しています。まず、文字列のリストと入力としての文字列を使用してメソッドを使用し、次に整数のリストと整数を使用してメソッドを使用します。コンソール出力:

[New String, New String, New String] [888, 888, 888]
ジェネリック メソッドがなく、 30 の異なるクラスに対してfill()メソッドのロジックが必要な場合を想像してください。異なるデータ型に対して同じメソッドを 30 回も記述する必要があります。しかし、汎用メソッドのおかげで、コードを再利用できます。:)

汎用クラス

標準 Java ライブラリで提供される汎用クラスに限定されず、独自のクラスを作成することもできます。簡単な例を次に示します。

public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Old String");
       System.out.println(stringBox.get());
       stringBox.set("New String");

       System.out.println(stringBox.get());
      
       stringBox.set(12345); // Compilation error!
   }
}
Box <T>クラスはジェネリック クラスです。作成時にデータ型 ( <T> )を割り当てると、その中に他の型のオブジェクトを配置できなくなります。これは例で見ることができます。オブジェクトを作成するときに、それが文字列で動作することを示しました。

Box<String> stringBox = new Box<>();
コードの最後の行で、ボックス内に数値 12345 を入力しようとすると、コンパイル エラーが発生します。それはとても簡単です!独自のジェネリック クラスを作成しました。:) これで今日のレッスンは終了です。しかし、私たちはジェネリック医薬品に別れを告げているわけではありません。次のレッスンでは、より高度な機能について説明しますので、やめないでください。) 学んだ内容を強化するには、Java コースのビデオ レッスンを視聴することをお勧めします。
学業の成功を祈って!:)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION