5 章
ポリモフィズム
要点
- ポリモフィズムの動きをするのは
インスタンスメソッド
のみ インスタンス変数
,staticメンバ
は参照型に依存するため注意
public class Main {
public static void main(String[] args) {
Super o = new Sub();
o.print(); // 1 Sub
o.sPrint(); // 2 Super
System.out.println(o.name); // 3 Super
}
}
class Super {
String name = "Super";
void print() { System.out.println(name); }
static void sPrint() { new Super().print(); }
}
class Sub extends Super {
String name = "Sub";
void print() { System.out.println(name); }
static void sPrint() { new Sub().print(); }
}
解説
- print() はインスタンスメソッドのため、ポリモフィズムの動きをします。よって Sub が出力
- sPrint() は static メソッドのため、参照型に依存するため、13 行目が呼ばます。よって Super が出力
- インスタンス変数も参照型に依存します。よって Super が出力
まとめ
- インスタンスメソッド:注視する箇所は参照先のオブジェクト
- サンプルコード:3 行目の
new Sub();
- サンプルコード:3 行目の
- インスタンスメソッド以外:注視する箇所は参照型
- サンプルコード:3 行目の
Super o
- サンプルコード:3 行目の
super() と コンストラクタ
super() でスーパークラスのコンストラクタを呼び出す際、明示的にコンストラクタを指定しない場合、常に引数なしのコンストラクタが呼ばれる
ため注意。
コンストラクタを指定しない場合
public class Main {
public static void main(String[] args) {
new Sub(); new Sub(555); // Super() Sub() Super() Sub(int n)
}
}
class Super {
public Super() { System.out.print("Super() "); }
public Super(int n) { System.out.print("Super(int n) "); }
}
class Sub extends Super {
public Sub() { System.out.print("Sub() "); }
public Sub(int n) {
System.out.print("Sub(int n) ");
}
}
コンストラクタを指定する場合
public class Main {
public static void main(String[] args) {
new Sub(); new Sub(555); // Super() Sub() Super(int n) Sub(int n)
}
}
// ... 変更なしのため割愛
class Sub extends Super {
public Sub() { System.out.print("Sub() "); }
public Sub(int n) {
super(555); // 明示的にコンストラクタを指定
System.out.print("Sub(int n) ");
}
}
抽象クラス
要点
抽象メソッド
がメンバに存在する場合、クラスは抽象クラスでなければならない
- 抽象クラスの具象メソッドはオーバーライド任意
抽象メソッド
は必ずサブクラスでオーバーライドしないといけない- ただし、
継承先のサブクラスも抽象クラス
の場合はオーバーライドは任意になる
class Super {
abstract void method(); // 1
}
abstract class Super {
public abstract void method();
}
abstract class Sub extends Super {} // 2
解説
- 抽象メソッドがメンバに存在しているため、クラスは抽象クラスでなければならない
- 継承先の Sub も抽象クラスのため、継承元の Super の抽象メソッドをオーバーライドしなくてもコンパイルエラーにはならない
インタフェース
要点
// 【1】 インタフェースのアクセス修飾子は 'public' のみ
protected interface MyInter {} // 【😡】 protected は指定できない
// 【2】 メンバ変数は強制的に 'public static final' になるため '初期化' を行う必要がある
interface MyInter {
int num; // 【😡】 初期化を行っていない
}
// 【3】 抽象メソッドには強制的に 'public abstract' が付く
interface MyInter {
protected void method(); // 【😡】 抽象メソッドは 'public' のみしか許可されていない
}
// 【4】 デフォルトメソッドのアクセス修飾子は 'public' のみ
interface MyInter {
protected default void method() {} // 【😡】 指定できるのは public のみ
default static void method(int num) {} // 【😡】 デフォルトメソッドでは static修飾子 は使えない
}
// 【5】 staticメソッドのアクセス修飾子は、指定されていなければ 'public' が付く
interface MyInter {
protected static void method(int num) {} // 【😡】 protectedは使用できない
private static void method(String str) {} // 【😊】 private は使用可能
}
ひっかけ問題で多いパターン
interface A {
void x(int num);
}
abstract class B {
public void x(int num) {} // 1
public abstract void y();
}
class C extends B implements A {
public void y() {} // 2
}
解説
- インタフェース A の 抽象メソッド x() は 抽象クラス B で実装されていると解釈されます
- よって、具象クラス C では 抽象クラス B の 抽象メソッド y() を実装するだけとなる
List インタフェース
List<String> list = new ArrayList<>(Arrays.asList("foo", "bar", "baz"));
要点
- 要素の追加や削除ができる
- 要素の重複ができる
Arrays.asList() について
- 引数で配列を受け取り、リストを返す
固定サイズのリスト
に変換する上書きは可能
だが、追加や削除はできない
(もし、追加や削除を行った場合は、実行時エラーが発生する)
😡
public class Main {
public static void main(String[] args) {
String[] ary = { "foo", "bar", "baz", "hogehoge" };
List<String> list = Arrays.asList(ary); // 固定サイズのリスト
if (list.removeIf(str -> str.length() >= 3)) { // 要素を削除すると実行時エラーが発生
System.out.println(list);
}
}
}
😊
public class Main {
public static void main(String[] args) {
String[] ary = { "foo", "bar", "baz", "hoge" };
List<String> list = new ArrayList<>(Arrays.asList(ary)); // ArrayList に変換することで要素の追加や削除ができる
if (list.removeIf(str -> str.length() <= 3)) {
System.out.println(list);
}
}
}
ラムダ式
要点
- ラムダ式から参照されるローカル変数は
final(定数)
でなければならない - そのため、ラムダ式のスコープ外で参照元の値が変更することはできない
public class Main {
public static void main(String[] args) {
int i = 555;
Supplier<Integer> s = () -> i;
i++; // ラムダ式のスコープ外で参照元の値が変更することはできない
System.out.println(s.get()); // 555
}
}