かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

lookupしてみる

英語がさっぱりできないのにRich Client Programming PLUGGING INTO THE NETBEANS PLATFORMを買ってしまった。

とりあえず、NetBeansのモジュールのプログラムを覗いてると良く出てくるLookupについて。
Lookupの機能自体はすごく単純でjarを1つクラスパスに追加するだけで使えるようになるらしい。

そのjarとは、NB_HOME/platform7/lib/org-openide-util.jarらしい。
(NetBeans6の場合のパスです。NetBeans5系は多分platform6とかのはず)

Javaアプリケーションプロジェクト作成

とりあえず、さくっとお試しするためにJavaプリケーションのプロジェクトを作成。
ライブラリにorg-openide-util.jarを追加する。

Mainクラスに下のようなコードを書いて実行できることを確認!

package okazuki.lookup;

import org.openide.util.Lookup;

public class Main {
    
    public static void main(String[] args) {
        Lookup lookup = Lookup.getDefault();
        System.out.println(lookup);
    }
    
}

実行結果はこんな感じ。

ProxyLookup(class=class org.openide.util.Lookup$DefLookup)->[MetaInfServicesLookup[sun.misc.Launcher$AppClassLoader@a90653], SimpleLookup[sun.misc.Launcher$AppClassLoader@a90653]]

ちゃんと何か取れとるらしい。

そもそもLookupって?

何かしらんがオブジェクトが欲しいときに探してくれるっぽい。
たとえばLookupが中に持ってるオブジェクト全部ほしいときは下のようになる。
NetBeans6と、それ以前では書き方が違うみたいなので2通りかいてます。

package okazuki.lookup;

import java.util.Collection;
import org.openide.util.Lookup;

public class Main {
    
    public static void main(String[] args) {
        Lookup lookup = Lookup.getDefault();
        
        {
            // NetBeans6
            Collection objs = lookup.lookupAll(Object.class);
            for (Object obj : objs) {
                System.out.println(obj);
            }
        }
        
        {
            //NetBeans5.x
            Lookup.Template template = new Lookup.Template(Object.class);
            Lookup.Result results = lookup.lookup(template);
            Collection objs = results.allInstances();
            for (Object obj : objs) {
                System.out.println(obj);
            }
        }
        
    }
}

実行するとこんな感じ。

sun.misc.Launcher$AppClassLoader@a90653
sun.misc.Launcher$AppClassLoader@a90653

どうやら1つしかオブジェクト持ってないらしい。
この中に俺様オブジェクトを追加するには、META-INF/services以下にファイルを置くとできるって書いてある。


その前に登録するためのクラスを定義!!
お決まりのハローワールドです

// Greeter.java
package okazuki.lookup;

public interface Greeter {
    void greet();
}

--------------------------------
// GreeterImpl.java
package okazuki.lookup;

public class GreeterImpl implements Greeter {
    public void greet() {
        System.out.println("Hello world");
    }
}

ソースフォルダ以下にMETA-INF/services/okazuki.lookup.Greeterという名前のファイルを作る。
中身は下のとおり。

okazuki.lookup.GreeterImpl

そしてメインメソッドを下のように変更する。
取得するクラスをGreeterにしている。

package okazuki.lookup;

import java.util.Collection;
import org.openide.util.Lookup;

public class Main {
    
    public static void main(String[] args) {
        Lookup lookup = Lookup.getDefault();
        
        {
            // NetBeans6
            Collection<? extends Greeter> objs = lookup.lookupAll(Greeter.class);
            for (Greeter obj : objs) {
                obj.greet();
            }
        }
        
        {
            //NetBeans5.x
            Lookup.Template<Greeter> template = new Lookup.Template<Greeter>(Greeter.class);
            Lookup.Result<Greeter> results = lookup.lookup(template);
            Collection<? extends Greeter> objs = results.allInstances();
            for (Greeter obj : objs) {
                obj.greet();
            }
        }
        
    }
}

実行するとちゃんとHello worldが出てる。

Hello world
Hello world

んじゃ差し替えてみよう

GreeterImpl以外のファイルも付け加えてみよう。
たとえば、GreeterImpl2というクラスを作ったとする。
中身は下のとおり

package okazuki.lookup;

public class GreeterImpl2 implements Greeter {
    public void greet() {
        System.out.println("こんにちは世界");
    }
}

そして、META-INF/services/okazuki.lookup.Greeterの中身にGreeterImpl2を付け加える

okazuki.lookup.GreeterImpl
okazuki.lookup.GreeterImpl2

この状態でリビルドして実行すると…

Hello world
こんにちは世界
Hello world
こんにちは世界

おぉ〜!ちゃんと出た!!!
DIコンテナあたりを使うともっと高度な事ができるけど、そんな機能イラネってときには使えるかな?

ちなみにインスタンスは何個作られるの?

GreeterImplとGreeterImpl2のコンストラクタにコードを仕込んで試してみた。
実行結果は下のようになった。

GreeterImpl::GreeterImpl
GreeterImpl2::GreeterImpl2
Hello world
こんにちは世界
Hello world
こんにちは世界

2回lookupしてるにも関わらず、1個だけインスタンスが作られてる。