Java コールバックの必要性と実装方法(超初心者)

こんにちは、kisseです。
Javaでコードを書いていたとき。
友人が「そこ、コールバック使った方が良くない?」って言いました。
でも、僕「コールバックとは?」という状態だったので、コールバックとは何かを調べ、実装できるようになることにしました。

本記事は、コールバックとは何かわからない人が、Javaでコールバックを実装できるようになることを目標にしてます。
つまり、僕。笑
コールバック初心者が、コールバック初心者に語ります。

コールバック関数とは

コールバック関数とは、で検索するとたくさん詳しい説明があります。

UIの処理とその他の処理を非同期で行うようにすることでGUIを実装することが多いのですが、その他の非同期処理からUIの変更を行う場合(サーバークライアントで通信が発生した。ボタンがクリックされた。etc.)など、他のクラスの処理を呼ぶ必要が生じた場合に、コールバックを利用します。

コールバックの必要性

僕がコールバックを実際に使用したのは、クラスAのインスタンスからクラスBのインスタンスを生成し、クラスBのインスタンスからクラスAのインスタンスメソッドを呼ぼうとしたタイミングです。
通常、クラスBのインスタンスからそれを生成したクラスAのインスタンスに参照を持たないので、クラスBのインスタンスからクラスAのインスタンスメソッドを呼ぶことはできません。

これを解決するために僕は、クラスAのインスタンスへの参照をクラスBのインスタンスに渡す実装を行なっていました。
結局これで動作するプログラムになるのですが、問題点があります。
それは、クラスBのインスタンスから呼ぶべきではないクラスAのインスタンスメソッドを呼ぶことができてしまう点です。
以下に例を示します。

class SampleClassA {
    public static void main(String[] args) {
        new SampleClassA();
    }

    private SampleClassB sampleClassB;

    public SampleClassA() {
        sampleClassB = new SampleClassB();
        sampleClassB.setReference(this);
        sampleClassB.someMethod();
    }

    public void methodA() {
        // 呼ばれていいメソッド
        System.out.println("これは呼ばれていいメソッド");
    }

    public void methodB() {
        // 呼ばれちゃだめなメソッド
        System.out.println("呼ばれちゃだめなメソッドを呼ぶことができてしまってる。");
    }
}

class SampleClassB {
    private SampleClassA sampleClassA;

    public void setReference(SampleClassA arg) {
        sampleClassA = arg;
    }

    public void someMethod() {
        // SampleClassAのメソッドは全て呼べてしまう。-> よくない
        sampleClassA.methodA();
        sampleClassA.methodB();
    }
}

本来呼ばれるべきではないメソッドを呼ぶことができてしまいます。
コードが小さく、呼ばれるべきメソッド、そうでないメソッドを管理できているうちはいいですが、コードが大きくなって管理する人を自分1人じゃなくなったときの危険性が増大しますよね。

それを解消するために、コールバックが必要なのです。
コールバックを用いることで、クラスBのインスタンスが呼べるクラスAのインスタンスを制限することができます。
以下でその実装を簡単に解説します。

Javaにおけるコールバックの実装

avaにおいてコールバックを実装する場合、インタフェースを利用します。
インタフェースでコールバックで呼びたいメソッドを宣言し、コールバックされるクラスでそのメソッドを実装します。
以下に例を示します。

class ClassA implements ClassB.ICallBack{
    // クラスBのインスタンスを生成し、コールバックメソッドを持つクラス
    public static void main(String[] args) {
        new ClassA();
    }

    private ClassB classB;

    public ClassA() {
        classB = new ClassB();
        classB.setReference(this);
        classB.someMethod();
    }

    public void callbackMethod() {
        // インタフェースClassB.ICallBackの実装
        System.out.println("クラスBからコールバックされた!");
    }

    public void cannotCallFromClassB() {
        // インタフェースに記述されてないので、ClassBから呼ぶことができない。
    }
}

class ClassB {
    interface ICallBack {
        public void callbackMethod();
    }

    private ICallBack reference;

    public void setReference(ICallBack arg) {
        reference = arg;
    }

    public void someMethod() {
        reference.callbackMethod();
        // reference.cannnotCallFromClassB(); // エラーが出るよ
    }
}

上記のコードでは、reference.cannotCallFromClassB()メソッドはコメントアウトしてますが、このコメント外してもエラーがでます。
一方、ClassBから、callBackMethod()メソッドを呼ぶことができているので、メソッドの呼び出しを制限することができています。
このようにすることで、呼び出されるメソッドを適切に管理することができるようになります。

まとめ

コールバックを実装することで、自由にかつ安全性の高いコードを書けるようになります。

これは、あくまでも初心者のための記事なんで、まだコールバックわかんないっていう人はこちらから始めてください。
他の強いエンジニアの方が、もっといい実装方法を公開してるそうなので、コールバックと仲良くなってからみてみてください。

次にJava書く時には積極的に使うぞー。

最後まで読んでいただきありがとうございます!

あわせて読みたい