nickotaのあうとぷっと。

ため込むだけだった情報を世に発信する。

Udemy 「JavaSE8 インタフェース ラムダ式 ストリーム 集中コース」セクション1、2

今更ながらJava8を本格的に勉強する。

 教材URL:

https://www.udemy.com/course/javase8-h/

セクション1

コース全体の説明。割愛。

セクション2 型パラメータとジェネリクス

書き方のまとめ

//型パラメータが指定されたクラス
class Base01<T>{}
//境界値パラメータが指定されたクラス
class Base02<T extends Super01>{}
//Stringが指定されたコレクション
List<String> list02 = new ArrayList<>();
//型パラメータが指定されたコレクション
List<Base01> list03 = new ArrayList<>();

0201 型パラメータの種類と誤例

1. 型パラメータを表す文字の種類→処理対象

E: Element → 要素オブジェクト
K: key → キーオブジェクト
V: Value → 値を表すオブジェクト
T: Type → 一般的なクラスのオブジェクト
?: 型を指定しない → 全オブジェクト

型パラメータの使い分けに明確な判断はない。

2. プログラミング不可例
new T();

これはコンパイルエラー。
「EKVT?」 のような大文字は実際にクラスが存在しているわけではない。

List<E> list = new ArrayList<>();

Eというオブジェクトを保存するArrayListという意味。
同様に「E」というクラスは存在しないため、コンパイルエラー。

0202 型パラメータサンプルプログラム

public class Base01<T> {
     private T t;
     public Base01(T t) {
         this.t = t;
     }
     public T getT() {
         return this.t;
     }
     public void setT(T t) {
         this.t = t;
     }
     @Override
     public String toString() {
         return "Base01";
     }
}

Tが例えばString なら全てのTをStringと読み替える。

public class Base02<T extends Super01> {
     private T t;
     public Base02(T t) {
         this.t = t;
     }
     public T get() {
         return this.t;
     }
     public void set(T t) {
         this.t = t;
     }
     @Override
     public String toString() {
         return "Base02";
     }
}

T extends Super01 → 拡張型パラメータ
意味:「Super01というクラスであっても、Super01というクラスを拡張したクラスであっても”T”として保存できる。」

例)
    Super0102 extends Super0101
    Super0101 extends Super01

の時、Super0101もSuper0102もBase02のTになることができる。

0203 型パラメータを利用したオブジェクト生成

型パラメータの利点

  1. 同処理を行うクラスを、処理対象となるオブジェクトごとに作らずに済む。
  2. Javaの構文として利用。(後章にて解説)
public class S02L03 {
    public static void main(String[] args) {
        Base01<String> b01_1 = new Base01<>("Base01_String");
        Base01<Integer> b01_2 = new Base01<>(100);
        Base02<Super01> b02 = new Base02<>(new Super01());

        System.out.println(b01_1.get()); // Base01_String
        System.out.println(b01_2.get()); // 100
        System.out.println(b02.get()); // Super01

        System.out.println(b01_1); // Base01
        System.out.println(b01_2); // Base01
        System.out.println(b02); // Base02
	}
}

0204 型パラメータの構文

1.型パラメータ
<E> → Eのみ
<K,V> → KとV
2.境界値パラメータ
Super01  //スーパークラス
Sub0101 extends Super01  //サブクラス
Sub0102 extends Sub0102  //サブクラスのサブクラス

の時、適用範囲は

<E super Sub0102> → Object, Super01, Sub0101, Sub0102
<E extends Super01> → Super01, Sub0101, Sub0102

0205 境界値パラメータを利用したオブジェクト生成

public class S02L05 {
    public static void main(String[] args) {
        // 事前知識 class Base02<T extends Super01> {
        Base02<Super01> b02_super01 = new Base02<>(new Super01());
        Base02<Sub0101> b02_sub0101 = new Base02<>(new Sub0101());
        Base02<Sub0101> b02_sub0102 = new Base02<>(new Sub0102());

        System.out.println(b02_super01.get()); // Super01
        System.out.println(b02_sub0101.get()); // Sub0101
        System.out.println(b02_sub0102.get()); // Sub0102
	}
}

0206 コレクションの基本的な操作

コレクションの型パラメータにスーパークラスを指定すると、
そのスーパクラス、および、スパークラスを継承するクラスのオブジェクトがコレクションに保存できる。

public class S02L06 {
    public static void main(String[] args) {
        List<Super01> list01 = new ArrayList<>();
        list01.add(new Super01());
        list01.add(new Sub0101());
        list01.add(new Sub0102());

        System.out.println(list01.get(0)); // Super01
        System.out.println(list01.get(1)); // Sub0101
        System.out.println(list01.get(2)); // Sub0102
        System.out.println(list01); // [Super01, Sub0101, Sub0102]

        Super01 s01 = list01.get(2); // 取り出すときはスーパークラス
    }
}

JavaAPIドキュメントを見るとArrayList< E>のようにちゃんと「E」と書いてある。
意味:Super01をスーパークラスにもつ要素は全て保存できる。

取り出す際(s01)はスーパークラスを書いておけば、
継承している全てのクラスを記述する必要がなくなる。

0207 ジェネリックス型に型パラメータを利用したプログラム

目的:以下のようなコレクションを一個のクラスで実現したい
ArrayList<String>
ArrayList<Integer>
ArrayList<Super01>

注意点:万能ではない。三つで共有できるメソッドのみを実装できる場合のみに有効。

public class BaseList01<E> {
    private List<E> e;
    // 新規作成
    public void create() {
        this.e = new ArrayList<E>();
    }
    public List<E> get() {
        return this.e;
    }
    // boolean add(E e)
    public void add(E element) {
        e.add(element);
    }
    // 初期化
    // static<T> List<T> asList(T...a)
    public void setArray(E[] array) {
        this.e = Arrays.asList(array);
    }
    @Override
    public String toString() {
        return e.toString();
    }
}

これらのメソッドはString, Integer, Super01三つどれでも使えるメソッド

0208 型パラメータを持つコレクションのオブジェクト生成および要素表示

public class S02L08 {
    public static void main(String[] args) {
        // 1. Stringのみを保存するArrayListの生成
        BaseList01<String> list01 = new BaseList01<>();
        list01.create();
        list01.add("DOG");
        list01.add("CAT");
        System.out.println(list01); // [DOG, CAT]

        // 2. Super01およびSuper01を継承するオブジェクトを保存するArrayListの生成
        BaseList01<Super01> list02 = new BaseList01<>();
        list02.create();
        list02.add(new Super01());
        list02.add(new Sub0101());
        list02.add(new Sub0102());
        System.out.println(list02); // [Super01, Sub0101, Sub0102]
    }
}

こうすることで複数のクラスを作る必要がなくなる
ちなみに一つのlistで複数の型を持てるわけではない。

BaseList01<String> list01 = new BaseList01<>();
list01.create();
list01.add("DOG");
list01.add("CAT”);
list01.add(1);

は最初に

BaseList01<String> list01 = new BaseList01<>();

のように初期化しているからString縛り。

BaseList01<E> list01 = new BaseList01<>();

も思いつくが、「E」というクラスは存在しないため、コンパイルエラー。
同じBaseList01というクラスでStringもIntegerもSuper01も扱えたのが注目ポイント。

0209 型パラメータ(?)を利用したプログラム

0208からの続きで、list01とlist02を同時に引数として扱えるようにする方法。

public class S02L09 {
    public static void main(String[] args) {
        BaseList01<String> list01 = new BaseList01<>();
        list01.create();

        String[] Data01 = { "DOG", "CAT" };
        list01.setArray(Data01);
        printList(list01); // [DOG, CAT]

        BaseList01<Super01> list02 = new BaseList01<>();
        list02.create();

        Super01[] Data02 = { new Super01(), new Sub0101(), new Sub0102() };
        list02.setArray(Data02);
        printList(list02); // [Super01, Sub0101, Sub0102]
    }

    public static void printList(BaseList01<?> pData) {
        System.out.println(pData);
    }
}

ちなみに

public static void printList(BaseList01<E> pData) {

は同様にコンパイルエラー。

0210 メソッドの構文について

なし

0211 戻り値の型宣言が指定された構文

以下の構文における先頭<R>は戻り値の型宣言。

<R> R getR2(T t, R r){}

以下はコンパイルエラー。クラス内では「T」しか使わないと宣言しているため。

class SampleType01<T>{
    R getR1(T t, R r){}
}

以下はコンパイル可能。
<R>はこのメソッド内で「R」を使いますよーという宣言

<R> R getR2(T t, R r){}

ちなみに以下のようにするとメソッドの最初にがついていなくても
コンパイルが通るようになる。 

class SampleType01<T,R>{}