Java Silver SE11 7章
黒本をもとに学んだことをアウトプットしていきます。
主に問題を解いていて、間違えた箇所もしくは知らなかった内容になります。
継承 (extends)
元となるクラスの差分だけを拡張機能として、新たなクラスを宣言できる。
public class クラス名 extends 継承元のクラス名{
継承元のクラスとの差分メンバ
}
2つのクラスの継承関係
- 継承元のクラス: スーパークラス、親クラス
- 新たに定義されるクラス: サブクラス、子クラス
ルール
- 全てを継承するわけではない
- コンストラクタ
- privateなフィールド、メソッド
- 宣言にfinalがついているクラスは継承できない
- 宣言にfinalをついているメソッドはオーバーライドできない
- フィールドを参照した場合には、変数の型で宣言された方を使う
- メソッドを呼び出した場合には、メソッド内の指示に従う
public class A {
String val = "A";
void print() {
System.out.print(val);
}
}
public class B extends A{
String val = "B";
}
public class Main {
public static void main(String[] args) {
A a = new A();
A b = new B();
System.out.print(a.val); // A
System.out.print(b.val); // A
a.print(); // A
b.print(); // A
B c = new B();
System.out.print(c.val); // B
c.print(); // A
}
}
A b = new B();
はAクラスで扱うように指示されている。
そのためAクラスのフィールドの値が使用される。
抽象クラス (abstract)
継承されることが前提のクラス。
継承する元クラスの内容が確定していない場合(今後そうするか決まっていない)「abstract」を記述することで、抽象クラスとして指定される。
public abstract class クラス名{
// 子クラスが引き継ぐ
public void sample1(){
// 処理
}
// 子クラスでオーバーライドにより実装する
public abstract void sample2();
}
ルール
- 抽象クラスは、インスタンス化できない
- 抽象クラスを継承して、新たな抽象クラスを定義できる
- 抽象メソッドは、オーバーライドにより実装し直さなければならない
- 継承先が抽象メソッドの場合は、実装する必要はない
- 抽象メソッドを実装するのはあくまで、実装する子クラスの役目
- 抽象メソッドを含むクラスは、abstractを記述し抽象クラスにする必要がある
- 未実装のメソッドを含んだまま、インスタンスを生成できないから
- フィールドは定義可能
オーバーライド
親クラスで定義されたメソッドを子クラスで再定義すること。
ルール
- シグニチャ(引数の型、並び、数)が同じであること
- 戻り値型が同じもしくは、サブクラスであること
- アクセス修飾子は同じか、より緩いものにする(厳しくはできない)
public class Parent {
public void greet() {
System.out.println("hello");
}
}
public class Child extends Parent{
public void greet() {
System.out.println("evening");
}
public void play() {
System.out.println("play");
}
}
public class Main {
public static void main(String[] args) {
Child ch = new Child();
ch.greet(); // evening
Parent pa = new Child();
pa.greet(); // evening
pa.play(); // コンパイルエラー Parentクラスでは定義されていない
}
}
インタフェース (interface)
抽象クラスのうち、抽象メソッドしか持たないものをインタフェースとして扱える。
型を指示することで、宣言した変数の「扱い方」を決める。
つまりインスタンスの種類と変数の型を分けて考えることができる。
例:10という数値は、int型でもdouble型でも扱える
public interface class インタフェース名{
// インスタンス化しなくても使えるので、実装可能
// public static finalは省略できる
public static final double PI = 3.14;
// public abstract は省略可能
public abstract void hello();
// コンパイルエラー {}により処理なしという中身を持っている
public abstract void hello(){
}
}
public class クラス名 implements インタフェース名{
// 処理
}
// 多重実現
public class クラス名 implements インタフェース名1, インタフェース名2{
// 処理
}
ルール
- アクセス修飾子を記述しなくても、自動的にpublicで修飾される
- protect、privateでは修飾できない
- インタフェースは、インスタンス化できない
- インタフェースを実装する子クラスを作り、「implements」を記述することでインスタンス化する
- 実装したクラスが持つべき抽象メソッドを宣言できる
- つまり子クラスに対して、インタフェースに定められらメソッドの実装を強制できる
- メソッドの中身は記述できない
- フィールドは記述できるが、条件がある
- finalを使い、値が変更されないこと(定数)
- staticを使い、インスタンスが生成されなくとも使えること
defaultメソッド
インタフェースのメソッドをdefaultで修飾すると、メソッドの中身を実装できる。
ただし、java.lang.Objectクラスに定義されているメソッドは、defaultでオーバーライドできずコンパイルエラーになる。
例:toString()
default 戻り値型 メソッド名(引数の型 引数名){
// 処理
}
例を以下に記述する。
public interface A {
default void sample() {
System.out.println("sample");
}
}
public class B implements A {
}
public class Main {
public static void main(String[] args) {
A a = new B();
a.sample(); //sample
}
}
継承関係のコンストラクタ
子クラスのインスタンスを生成するときは、先に親クラスのコンストラクタを実行する必要がある。
(親クラスは基盤の役割をしており、その差分を子クラスとして追加するイメージ)
public class Parent {
public Parent() {
System.out.println("A");
}
public Parent(String val) {
this();
System.out.println(val);
}
}
public class Child extends Parent{
public Child() {
super("B");
System.out.println("C");
}
public Child(String val) {
this();
System.out.println(val);
}
}
public class Main {
public static void main(String[] args) {
new Child("D"); // ABCD
}
}
多様性
簡単なコードで検証してみました。
public interface A {
void methodA();
}
public class B implements A {
@Override
public void methodA() {
System.out.println("OverrideしたmethodAを使用");
}
public void methodB() {
System.out.println("methodBを使用");
}
}
public class C {
public void methodC() {
System.out.println("methodCを使用");
}
public void methodZ() {
System.out.println("methodZを使用 Cクラス");
}
}
public class D extends C{
public void methodD() {
System.out.println("methodDを使用");
}
public void methodZ() {
System.out.println("methodZを使用 Dクラス");
}
}
public class Main {
public static void main(String[] args) {
A a = new B();
a.methodA();
// a.methodB(); // コンパイルエラー
C c = new D();
c.methodC();
// c.methodD(); // コンパイルエラー
c.methodZ();
}
}
OverrideしたmethodAを使用
methodCを使用
methodZを使用 Dクラス
C c = new D();
のメソッド「methodD()」
について
インスタンスはDクラスを元に作成されている。
しかし変数c
は「Cクラスの型」として扱われているので、
Cクラスにないメソッド「methodD()」を使用するとエラーになる。
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
メソッド methodD() は型 C で未定義です
C c = new D();
のメソッド「methodZ()」
について
「Cクラスの型」として扱われているので、メソッド「methodZ()」を使用した時、Cクラスのメソッドが実行されるように思われる。
しかし、インスタンス自体はDクラスなのでメソッド「methodZ()」が呼ばれさえすれば、
Dクラス内のメソッドが実行される。