【コラム】

ダイナミックObjective-C

6 Cocoa-Javaの挑戦とは? - 似て非なるセレクタとリフレクション

    木下誠  [2005/09/14]

    CocoaはObjective-Cを用いてAPIが提供されている。だが、この非常にマイナーな言語だけでは辛いと思ったのか、JavaによるAPIも存在する。つまり、JavaとObjective-Cをつなぐブリッジを実現したのだ。ともにオブジェクト指向言語であるこの2言語だが、当然いろいろな違いもある。

    その違いを吸収するために、Cocoa-Javaブリッジでは様々な工夫が行われている。特に苦心の跡が見られるのは、前回説明したターゲット/アクションパラダイムのJava実装だ。今回は、ここを詳しく解説しよう。動的なObjective-Cと、静的な言語の考え方の違いが明瞭に現れているポイントだ。

    似て非なるObjective-Cのセレクタと、Javaのリフレクション

    Cocoaのターゲット/アクションパラダイムとは、一言で言えば、任意のオブジェクトの任意のメソッドを呼び出すもの。つまり、オブジェクトとメソッドを表す変数を指定し、それを呼び出すことができれば実現できることになる。

    Objective-Cでは、オブジェクトを表すのにid型を、メソッドを表すのにセレクタを利用している。これらを使えば、次のようにアクションを呼び出すことができる。

    - (void)invokeTarget:(id)target withAction:(SEL)action
    {
        [Target performSelector:action withObject:self];
    }

    この呼び出し方は、JavaのリフレクションAPIに似ていることに気づくだろう。リフレクションには、メソッドを表すMethodオブジェクトがある。これを利用することはできないだろうか。次のような感じでだ。

    import java.lang.reflect.*;

    public void invoke(Object target, Method action) {
        try {
            action.invoke(target, new Object[] { this });
        }
        catch (Exception e) {
        }
    }

    なるほど。たしかにセレクタと似ている。だが実は、両者には決定的に違うところがある。それは、Methodオブジェクトが特定のクラスに結びついているという点だ。Methodは、次のようにメソッド名と引数の型を指定してClassから取得する。

    Object obj;
    Method method;
    try {
        method = obj.getClass().getMethod("getTitle", new Class[] { Object.class });
    }
    catch (Exception e) {
    }

    Classから取り出す、ということに注目してほしい。つまり、このMethodオブジェクトはこのクラス専用であり、他のクラスに適用することができない。クラスAから取得したMethodオブジェクトは、たとえ同名のメソッドがあったとしても、クラスBに適用することはできない。

    また、存在しないメソッドのためのMethodオブジェクトを作ることもできない。getMethodを呼び出す時点で存在がチェックされ、例外が発生してしまう。

    NSSelectorによる実現

    ターゲット/アクションパラダイムでは、ターゲットとアクションを切り離して指定するところが肝である。Methodオブジェクトでは、そのまま利用することはできない。そこでCocoa-Javaでは、新たにNSSelectorというクラスを導入することにした。

    NSSelectorは、JavaでObjective-Cのセレクタ機能を擬似的に実現するためのクラスである。次のように使うことになる。

    NSSelector selector;
    selector = new NSSelector("methodName", new Class[] { Object.class });

    public void invoke(Object target, NSSelector action) {
        try {
            action.invoke(target, new Object[] { this });
        }
        catch (Exception e) {
        }
    }

    メソッド名と引数を指定してインスタンスを作り、invokeメソッドを使って呼び出すところなどはMethodと同じである。違いは、インスタンスを作成するときに、メソッドが所属するクラスを指定しないところだ。NSSelectorは、特定のクラスからは独立して作られる。また、例外が発生しないところも注目だ。この時点では、メソッドの存在は特にチェックされていないことを意味している。

    このような機能を満たすための、NSSelectorのinvokeのコードを、擬似的に想像してみよう。次のようなコードで実現できるだろう。

    public Object invoke(Object obj, Class[] args) throws ... {
        Method method;
        method = obj.getClass().getMethod(methodName, args);
        return method.invoke();
    }

    つまり、呼び出し自体は、Methodオブジェクトを使ったものと変わらない。違いは、Methodの取得を実行時に行っている、という点だ。

    ここに、動的な言語と静的な言語の思想の違いを見ることができるだろう。Javaでは、コンパイルの時点でできだけ物事を決定しておきたい。メソッドは、あるクラスに存在するものからのみ、取得することができる。それに対してObjective-Cでは、できるだけ評価を後にのばす。セレクタは実装と独立した形で取得でき、呼び出す時点で初めて評価される。

    この2つの言語をつなぐには、今回解説したような工夫が必要になるのだ。

    新着記事

    特設サイトの情報

      求人情報

      人気記事

      一覧

      イチオシ記事

      新着記事

      特別企画

      転職ノウハウ

      あなたの仕事適性診断

      4つの診断で、自分の適性を見つめなおそう!

      Heroes File ~挑戦者たち~

      働くこと・挑戦し続けることへの思いを綴ったインタビュー

      はじめての転職診断

      あなたにピッタリのアドバイスを読むことができます。

      転職Q&A

      転職に必要な情報が収集できます

      スカウト転職する

      企業からアプローチのメッセージが届きます。

      マイナビニュースマガジン