【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);
}
}
こんな感じ。
ついでに、スタティックメソッドもインタフェース内に作ることができるけど、作るより使うことの方が多そう。必ず、
インタフェース名.スタティックメソッド名()
の形で使うことだけ覚えておく。スタティックメソッドはオーバーライド出来ない。
とりあえずわーっとここまで。
この記事が気に入ったらサポートをしてみませんか?