1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Java】Generics(ジェネリクス)を理解する

Last updated at Posted at 2021-07-10

はじめに

現在の開発実務ではKotlinを利用しているものの、たびたびJavaの知識が必要に感じることがある。
(定義ジャンプしたときとか、Kotlinが便利すぎて色々省略されていて、「なんでこうなってんの??」って思うときとか、、そう言うとき)

なのでJavaについても理解を深めたい。

ジェネリクスとは

  • クラス宣言時:そのクラス内で利用する要素の「仮の型名(Eなど)」を使っておく
  • クラス利用時:「仮の型名(Eなど)」をStringなどの「実際の型」に置き換えて利用する

と言う仕組み

ジェネリクスのメリット

型を限定しないため汎用的なクラスを定義することができるとともに、クラスを利用する際は利用者自身で型安全を確保することができる。

ジェネリクスの注意点

  • ジェネリクスの型に int などの基本データ型は利用不可(例:Generics<int>コンパイルエラーになる)
  • ジェネリクスを用いたクラスの配列を作ることはできない。
  • Throwableの子孫クラス(例外クラス)では、ジェネリクスを用いることができない

実際にコードを動かして確認してみる

ジェネリクスを使った例

// このクラスでは E の型は利用する際に決めることする
public class Generics<E> {
  private E obj;
  
  public Generics(E obj) {
    this.obj = obj;
  }
  
  public E getObj() {
    return obj;
  }
}

class Main {
  public static void main(String[] args) {
    // 利用する際に E を String として利用することに決める
    Generics<String> generics = new Generics<>("2021");
    
    String obj = generics.getObj();
    System.out.println(obj);  // 出力結果: 2021
  }
}

ジェネリクスを使わずにObject型で同じことをしてみる

public class NoGenerics {
  // ジェネリクスを利用せずに Object型 で利用する
  private Object obj;
  
  public NoGenerics(Object obj) {
    this.obj = obj;
  }
  
  public Object getObj() {
    return obj;
  }
}


class Main {
  public static void main(String[] args) {
    // String型の引数obj としてインスタンス生成する
    NoGenerics noGenerics = new NoGenerics("2021");

    // キャストして取得する。キャストしないと String として利用できない。
    String obj = (String) noGenerics.getObj();
    System.out.println(obj); // 出力結果: 2021
  }
}

ジェネリクスを利用する場合に比べて、これの何が問題か? というのを考えてみると。

  1. キャストが面倒くさい
  2. 誤ったキャストによるエラーが発生する危険性がある(ClassCastException

2のエラー危険性があることが非常によろしくない。開発時にコンパイルエラーで危険性を教えてくれない。

ClassCastException になってしまう例

// ジェネリクスを利用しないクラス
public class NoGenerics {
  // Object型 だからなんでも入れられちゃう
  private Object obj;
  
  public NoGenerics(Object obj) {
    this.obj = obj;
  }
  
  public Object getObj() {
    return obj;
  }
}

class Main {
  public static void main(String[] args) {
    // String型の引数obj としてインスタンス生成する
    NoGenerics noGenerics = new NoGenerics("2021");

    // もしこの引数を Integer型 だと勘違いして利用しようとすると当然エラーが発生する
    Integer obj = (Integer) noGenerics.getObj(); // ここで ClassCastException !!!
    System.out.println(obj);
  }
}

この例だと簡単な例なのでStringIntegerに勘違いして利用しようとすることはイメージしづらいかもないものの、実際の開発で色々な処理が絡んでくると実際にあり得ることである。

実型引数に継承関係に基づく制限をかける

以下のようにすることで継承関係に基づいて制限をかけることができる

Generics<? extends E> // E もしくは、 Eの子孫クラス
Generics<? super E> // E もしくは、 Eの先祖クラス

最後に

ジェネリクスなんて今更かもしれませんが、改めて勉強になりました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?