見出し画像

【Javaお勉強日記】総称型とデフォルトメソッドについてわーっとやる

本読んだだけだと覚えられなかったので復習がてらわーっとまとめます。

総称型とは

実際に使う時に、使う型を宣言できるタイプの型宣言のこと。
なんのこっちゃ、と思うけど、例えば、前回やったMapやListなんかで使われている。

Listを作る時

List<String> listTest = new ArrayList<>();

って書くけど、この

List<String>

の部分が、「実際に使う時に、使う型を宣言」している部分。

仮に、ArrayListクラスを1から自分で作ろうとしてみるとする。

で、さてフィールドを作る時に、「このArrayはStringを受け取るのか?int?doubleかも知れないしbooleanかもしれないぞ……?」と困ってしまう。

そんなときに、「総称型」で定義しておけば、実際にインスタンスを作る時に「String入れるから!」「int入れるから!」と決める、という事が可能になる。

※実際には、intやdoubleなどのプリミティブ型は直接入れられないので、ラッパークラスのIntegerやDoubleを使う
(最近のJava先生は自動置換してくれるってテキストには書いてあったけど、そなーりんとは怒った(´・ω・`))

総称型を持つクラスの作り方

public class MyLogger<T>{
   private T obj;
   public MyLogger(T obj){
       this.obj = obj;
   }
   public T getObj(){
       return this.obj;
   }
}

慣例ではTを使うことが多い。(KeyとValならKとVとか、戻り値型を総称型にするならRを使うとか、二種類の型を使うならTの次はSとかもよく使う)

クラス内で総称型にしたい所を、具体的な型名の代わりにTで置き、クラス名宣言の横に<T>を書くと総称型として使える。

インタフェースにも総称型を使える

public interface Output<T,R> {
   R print(T obj);
}

これで、T型を引数に受け取り、R型を返すメソッドprintを持つインタフェースOutputが作れる。

実装するときは

public class MyLogger implements Output<String,Integer>{
    // 略
    @Override
	public Integer print(String obj) {
    System.out.println(obj);
    // この流れでなんで1返すのか謎だけど「Integerを返す」のテストということで……
	return 1;
	}    
}

implementsした後、<>内に必要な数だけ型を書く。

VSCodeの補完機能がONになってれば、implementsと型指定書いた後、クラス名の下部に赤線が出るので、そこにカーソル合わせてQuickFixして貰うと、オーバーライドの部分がどさっと出てくるから便利。どっちが引数でどっちが戻り値型かとか解らなくならない。

特定のメソッドだけ総称型にする

public class MyLogger{
    // 略
    public <T> test(T obj){
        // 何かの処理
   }
}

って具合に、一部のメソッドだけ総称型にすることが出来る。

この場合、呼び出す時には型指定をせず

MyLogger myLogger1 = newMyLogger();
myLogger1.test("Stringでも")
// intでも
myLogger1.test(111)
// doubleでも
myLogger1.test(11.111)

という感じで呼び出すことが出来る。

さらにさらにインタフェースでも、一部のメソッドだけを総称型にできる。

public interface Output{
   <T> void print(T obj);
}

メソッドだけ総称型にして

public class MyLogger implements Output{
   // 略
   @Override
   public <T> void print(T message) {
       System.out.println(message + "::" + this.obj);
   }
}

総称型のメソッドとしてオーバーライドして

public class SymmetryTest {
   public static void main(String args[]){
       MyLogger myLogger = new MyLogger("ロガーのテスト");
       
       // printメソッドにはどんな型の引数でも渡せる
       myLogger.print("インタフェース経由で呼び出すよ");
       myLogger.print(111);  
   }
}

こう使う。

境界ワイルドカード型

<T> じゃなくて <? extends T>とか <? super T>とか書くことが出来る。

Tを継承している全ての型、あるいはTの全てのスーパークラス、という意味だけどあんまり深く考えなくて良い。<T extends S>みたいな書き方も出来るっぽい。

インタフェースに書けるデフォルトメソッド

インタフェース内に、抽象メソッドだけではなく、具象メソッドを書くことができる=デフォルトメソッド。

デフォルトメソッドはオーバーライドしなくてもエラーにならないし、オーバライドしてもいいので便利。

default修飾子を付けて使う。

public interface Output{
   default <T> void print(T obj){
       System.out.println(obj);
   }
}

こんな感じ。

ついでに、スタティックメソッドもインタフェース内に作ることができるけど、作るより使うことの方が多そう。必ず、

インタフェース名.スタティックメソッド名()

の形で使うことだけ覚えておく。スタティックメソッドはオーバーライド出来ない。

とりあえずわーっとここまで。

この記事が気に入ったらサポートをしてみませんか?