最終更新日:2021/01/30 原本2017-

インタフェース

Javaではインターフェイスという機能が利用できます. インタフェースとは のような特徴を持ちます.
ここから,簡単な例を用いて,インタフェースについて学びましょう. ここでは,配列の中身を整列する事を考えます. 整列のアルゴリズムはバブルソートや,選択ソート,挿入ソート,クイックソートなど様々にあります. また,整列の過程の観察するために,配列の要素を表示する事も考えましょう. 表示の仕方も様々です. そこで, という2つの機能を抽象化し,インタフェースで宣言し,それを実装したクラスでオーバーライドしてみたいと思います.
そのためのインフェースとして以下を用意します.

SortImp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package jp.ac.utsunomiya_u.is;
 
public interface SortImp {
    /**
     * 整列
     * @param x 整列対象
     */
    void sort(int[] x);
     
    /**
     * 配列の表示
     * @param x 配列
     */
    void show(int[] x);
}
3行目
interfaceキーワードでインタフェースを宣言しています.
8行目
整列するためのメソッドを宣言だけして,実装はしていません.
14行目
配列を整列するためのメソッドを宣言だけして,実装はしていません.
まず,バブルソートを実装してみましょう.
SortImp.javaと以下のBubbleSortImp.javaとSortImpTest.javaをコピーして動作を確認してください.

BubbleSortImp.java

1
2
3
4
package jp.ac.utsunomiya_u.is;
 
public class BubbleSortImp implements SortImp {
}

SortImpTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package jp.ac.utsunomiya_u.is;
 
public class SortImpTest {
 
    public static void main(String[] args) {
        int[] x = {8, 3, 6, 4};
 
        BubbleSortImp bubbleSort = new BubbleSortImp();
        sortTest(x, bubbleSort);     
    }
 
    static void sortTest(int[] x, SortImp sortImp) {
        sortImp.sort(x);
    }
}
8行目
BubbleSortImpのインスタンスを生成しています.
9行目
sortTestメソッドに,整列対象の配列とBubbleSortImpのインスタンスを渡します.
12-14行目
sortTestメソッドはstatic修飾子がついているので,静的メソッドです. 第2引数はSortImpクラスなので,アップキャストによって呼び出されています.
BubbleSortImp.javaはメソッドを実装していないためコンパイルエラーになります.

静的メンバー

クラスの持つ属性であるフィールドや振る舞いであるメソッドをあわせてメンバーと呼ぶ事があります. メンバーには が存在します. SortImpTestクラスの例では,SortImpTestのインスタンスを生成しなくてもsortTsetメソッドを呼ぶことが出来ます. 今まで,mainメソッドは必ず静的メソッドでした. これも同じ理由で,SortImpTestの一つのメソッドであるmainメソッドは,SortImpTestをインスタンス化しなくても呼ばれる事が出来ます.
また,Mathクラスは全てのフィールドとメソッドがstatic修飾子がついて構成されており,インスタンスメンバーはいないので,インスタンス化する必要はありません.
BubbleSortImp.javaを以下のように修正しましょう.

BubbleSortImp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package jp.ac.utsunomiya_u.is;
 
public class BubbleSortImp implements SortImp {
 
    @Override
    public void sort(int[] x) {
        int n = x.length;
        int t;
        for (int i = 0; i < n - 1; i++) {
            for (int j = n - 1; j > i; j--) {
                show(x);
                if (x[j - 1] > x[j]) {
                    t = x[j];
                    x[j] = x[j - 1];
                    x[j - 1] = t;
                }
            }
        }
        show(x);
    }
 
    @Override
    public void show(int[] x) {
        System.out.println("");
        for (int e : x) {
            System.out.print(e + " : ");
            for (int i = 0; i < e; ++i) {
                System.out.print("*");
            }
            System.out.println("");
        }
    }
}

出力

8 : ********
3 : ***
6 : ******
4 : ****

8 : ********
3 : ***
4 : ****
6 : ******

8 : ********
3 : ***
4 : ****
6 : ******

3 : ***
8 : ********
4 : ****
6 : ******

3 : ***
8 : ********
4 : ****
6 : ******

3 : ***
4 : ****
8 : ********
6 : ******

3 : ***
4 : ****
6 : ******
8 : ********
実装したメソッドはpublicでなければなりません.
整列のアルゴリズムもたくさんあるので,次に選択ソートを実装してみましょう.
以下のSelectionSortImp.javaをコピーして

SelectionSortImp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package jp.ac.utsunomiya_u.is;
 
public class SelectionSortImp implements SortImp {
 
    @Override
    public void sort(int[] x) {
        int n = x.length;
        int lowest, lowkey;
        int t;
 
        for (int i = 0; i < n - 1; i++) {
            lowest = i;
            lowkey = x[i];
            for (int j = i + 1; j < n; j++) {
                show(x);
                if (x[j] < lowkey) {
                    lowest = j;
                    lowkey = x[j];
                }
            }
            t = x[i];
            x[i] = x[lowest];
            x[lowest] = t;
        }
        show(x);
    }
 
    @Override
    public void show(int[] x) {
        System.out.println("");
        for (int e : x) {
            System.out.print(e + " : ");
            for (int i = 0; i < e; ++i) {
                System.out.print("@");
            }
            System.out.println("");
        }
    }
 
}
SortImpTest.javaの以下の部分を修正して実行してみましょう.

SortImpTest.javaの修正

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package jp.ac.utsunomiya_u.is;
 
public class SortImpTest {
 
    public static void main(String[] args) {
        int[] x = {8, 3, 6, 4};
        /*
        BubbleSortImp bubbleSort = new BubbleSortImp();
        sortTest(x, bubbleSort);
        */
        SelectionSortImp selectSort = new SelectionSortImp();
        sortTest(x, selectSort);
    }
 
    static void sortTest(int[] x, SortImp sortImp) {
        sortImp.sort(x);
    }
}

出力

8 : @@@@@@@@
3 : @@@
6 : @@@@@@
4 : @@@@

8 : @@@@@@@@
3 : @@@
6 : @@@@@@
4 : @@@@

8 : @@@@@@@@
3 : @@@
6 : @@@@@@
4 : @@@@

3 : @@@
8 : @@@@@@@@
6 : @@@@@@
4 : @@@@

3 : @@@
8 : @@@@@@@@
6 : @@@@@@
4 : @@@@

3 : @@@
4 : @@@@
6 : @@@@@@
8 : @@@@@@@@

3 : @@@
4 : @@@@
6 : @@@@@@
8 : @@@@@@@@
BubbleSortImp.showとSelectionSortImp.showは表示の記号が"*"であるか"@"であるかの違いだけです.
16行目のsortメソッドを呼んでいる部分は変更されていません. インタフェースを使ってポリモフィズムを実現しています.

抽象クラス

先の整列のインタフェースの実装では,BubbleSortImp.showとSelectionSortImp.showは表示の記号が"*"であるか"@"であるかの違いだけでした. もしかしたら,showメソッドは整列のインタフェースの実装では共通でも良いかもしれません. しかし,インタフェースで宣言されたメソッドは,それを実装したクラスで全てオーバーライドされなければなりません. オーバーライドすべきメソッドを選択できた方が柔軟な設計の可能性があがります. これを実現するために,C++と同様にJavaでも抽象クラスが利用できます. 抽象クラスは といった特徴を持ちます.
それでは整列のための抽象クラスを考えてみましょう. ここでは の要件を満たす抽象クラスを以下のように作成します.

SortExt.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package jp.ac.utsunomiya_u.is;
 
public abstract class SortExt {
 
    /**
     * 表示のためのマーク
     */
    private String mark = "*";
 
    /**
     * セッタ for マーク
     *
     * @param mark マーク
     */
    public void setMark(String mark) {
        this.mark = mark;
    }
 
    /**
     * ゲッタ for マーク
     *
     * @return マーク
     */
    public String getMark() {
        return mark;
    }
 
    /**
     * 整列の抽象メソッド
     *
     * @param x 整列対象の配列
     */
    abstract void sort(int[] x);
 
    /**
     * 配列の表示
     *
     * @param x
     */
    void show(int[] x) {
        System.out.println("");
        for (int e : x) {
            System.out.print(e + " : ");
            for (int i = 0; i < e; ++i) {
                System.out.print(mark);
            }
            System.out.println("");
        }
    }
}
3行目
abstractキーワードで抽象クラスを定義しています.
8行目
配列の要素の表示のためのマークを宣言し,初期化しています.
15-17, 24-26行目
マークのためのセッタとゲッタです.
33行目
abstract修飾子をつけて,抽象メソッドを宣言しています. SortExtを継承したサブクラスでは,メソッドsortを必ずオーバーライドしなければなりません.
40-49行目
配列の要素の表示関数です.
インタフェースと異なり,抽象クラスではーバーライドしなければならないメソッドと,必ずしもオーバーライドしなくても良いメソッドが混在出来ます. もちろん,抽象クラスはfinal修飾子がついたオーバーライド出来ないメソッドも含むことが出来ます. 当たり前ですが,final修飾子とabstract修飾子を同時につけることは出来ません.
では次に,抽象クラスSortExtクラスを継承したクラスを作成してみましょう. 前例と同様に,まずバブルソートで実装してみましょう.
SortExt.javaとそれを継承した以下のBubbleSortExt.javaとテスト用のSortExtTest.javaをコピーして動作を確認してみましょう.

BubbleSortExt.java

1
2
3
4
package jp.ac.utsunomiya_u.is;
 
public class BubbleSortExt extends SortExt {
}

SortExtTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package jp.ac.utsunomiya_u.is;
 
public class SortExtTest {
 
    public static void main(String[] args) {
        int[] x = {8, 3, 6, 4};
 
        BubbleSortExt bubbleSortExt = new BubbleSortExt();
        sortTest(x, bubbleSortExt);
    }
 
    static void sortTest(int[] x, SortExt sortExt) {
        sortExt.sort(x);
    }
}
これはコンパイルエラーになります. BubbleSortExtは抽象クラスSortExtを継承しているので,その全ての抽象メソッドをオーバーライドしなければなりません.
BubbleSortExtクラス内で以下のメソッドをオーバーライドして,動作を確認してみましょう.

BubbleSortExt.javaへの追加部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
void sort(int[] x) {
    int n = x.length;
    int t;
    for (int i = 0; i < n - 1; i++) {
        for (int j = n - 1; j > i; j--) {
            show(x);
            if (x[j - 1] > x[j]) {
                t = x[j];
                x[j] = x[j - 1];
                x[j - 1] = t;
            }
        }
    }
    show(x);
}