M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

JavaプログラマーからみたVala(5) 通知

原典は、“Vala for Java Programmers”(2011年3月13日取得)です。

******************************

通知メカニズム

Javaの場合: リスナーを使用します。(オブザーバ・パターンの実装です。)

public interface ClickListener {
    public void clicked(Clickable source);
}
public interface Clickable {
    public void addClickListener(ClickListener l);
    public void removeClickListener(ClickListener l);
}
public class MyButton implements Clickable {
    private List<ClickListener> clickListeners;

    public MyButton() {
        this.clickListeners = new ArrayList<ClickListener>();
    }

    private void fireClickListeners() {
        for (ClickListener listener : this.clickListeners) {
            listener.clicked(this);
        }
    }

    public void addClickListener(ClickListener l) {
        if (l != null) {
            this.listeners.add(l);
        }
    }

    public void removeClickListener(ClickListener l) {
        if (l != null) {
            this.listeners.remove(l);
        }
    }

    public void test() {
        fireClickListeners();    // リスナーに着火します。
    }
}
public class Demo {

    private class MyClickListener implements ClickListener {
        public void clicked(Clickable s) {
            System.out.println("handler C");
        }
    }

    public static void main(String[] args) {
        MyButton b = new MyButton();
        b.addClickListener(new ClickListener() {
            public void clicked(Clickable s) {
                System.out.println("handler A");
            }
        });
        b.addClickListener(new ClickListener() {
            public void clicked(Clickable s) {
                System.out.println("handler B");
            }
        });
        MyClickListener handlerC = new MyClickListener();
        b.addClickListener(handlerC);
        b.test();
        b.removeClickListener(handlerC);
    }
}

Valaの場合: シグナルを使用します。(signalキーワードを用いて定義し、.connect() .disconnect()で処理を設定します。)

public class MyButton : Object {

    public signal void clicked ();

    public void test () {
        clicked ();          // シグナルを発します。
    }
}

void handler_c (MyButton source) }
    stdout.printf ("handler C\n")
}

void main () {
    var b = new MyButton ();
    b.clicked.connect ((s) => stdout.printf ("handler A\n"));
    b.clicked.connect ((s) => {
        stdout.printf ("handler B\n")
    });
    b.clicked.connect (handler_c);
    b.test ();
    b.clicked.disconnect (handler_c);
}

プロパティ変更通知

Javaの場合:

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class DemoBean {

    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    private String title;

    public String getTitle() {
        return this.title;
    }

    public void setTitle(String title) {
        String old = this.title;
        this.title = title;
        this.pcs.firePropertyChange("title", old, title);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        this.pcs.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        this.pcs.removePropertyChangeListener(listener);
    }

    public static void main(String[] args) {
        DemoBean demo = new DemoBean();
        demo.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                System.out.println("Property " + evt.getPropertyName() + " changed");
            }
        });
        demo.setTitle("hello");
        demo.setTitle("world");
    }
}

Valaの場合: Glib.Objectのサブクラスであればnotifyシグナルを持っています。

public class Demo : Object {
    public string title { get; set; }
}

void main () {
    var demo = new Demo ();
    demo.notify.connect ((s, p) => stdout.printf ("Property %s changed\n", p.name));
    demo.title = "hello";
    demo.title = "world";
}

しかし、更新前の値を取得することはできません。
もし、特定のプロパティの変更のみ考慮するのであれば、次の構文が使用できます。

demo.notify["title"].connect ((s, p) => stdout.printf ("title changed\n"));

プロパティ変更通知は、CCode属性タグにより無効化できます。このタグの直後に宣言されたプロパティは、変更通知が無効化されます。

class MyObject : Object {

    // プロパティの値が変化してもnotifyシグナルは発行されません。
    [CCode (notify = false)]
    public int without_notification { get; set; }

    // notifyシグナルは発行されます。
    public int with_notification { get; set; }
}

例外

Javaの場合: クラスベースの例外が使用されます。

public class TemperatureException extends Exception {
}
public class TooColdException extends TemperatureException {
}
public class TooHotException extends TemperatureException {
}
public class ExceptionDemo {
    private void method() throws TemperatureException {
        throw new TooColdException("It's too cold!");
    }

    public static void main(String[] args) {
        try {
            method();
        } catch (TemperatureException e) {
            System.out.println(e.getMessage());
        }
    }
}

Valaの場合: 例外はエラー(errors)と呼ばれ、クラスベースではありません。ラッピングもありません。

// Javaの例外クラスと異なり、
// エラードメイン(error domain)は、
// 複数のエラーコード(error codes)をともなう。
errordomain TemperatureError {
    TOO_HOT,
    TOO_COLD
}

void method () throws TemperatureError {
    //  new エラードメイン.エラーコード(エラーメッセージ)
    throw new TemperatureError.TOO_COLD ("It's too cold!");
}

// エラーはキャッチするか伝播させる。
// 無視をするとコンパイラが警告する。
try {
    method ();
} catch (TemperatureError e) {
    stderr.printf ("Error: %s\n", e.message);
}

コンパイラはエラーの無視を警告こそすれ、それでコンパイルプロセスを中止するということはしません。これにより固有のエラーを処理するコードを書かずにプロトタイピングを行うことが可能になります。と同時に、空の例外処理ブロックの存在が忘れ去られるという事態を抑止することが期待できます。

エラーコードはis演算子でチェックできます。

if (e is TemperatureError.TOO_COLD) {
    // ...
}

******************************

JavaプログラマーからみたVala (6)につづく──

WindowsでValaを動かす

******************************
きょうはちょっと小休止。「JavaプログラマーからみたVala」は置いておいて、公式サイトにあった別のメモの翻訳を載せます。原典は、“Vala on Windows”(2011年3月14日取得)です。

******************************

Valaはいつもソースコードの形式でリリースされます。しかしValaコミュニティのメンバーが、Windows向けのバイナリを提供しています。.exeファイルとして提供されているインストーラにより、Windowsのための必要最低限のGNU環境(MinGW)──GlibやGTK+といったライブラリとともに、CコンパイラとValaコンパイラを提供します──をインストールできます。

インストールが終わると、次のようにしてValaプログラムをコンパイルできるようにります。

> valac hello.vala

GTK+ライブラリを使用したアプリを起動した際に、コンソールウィンドウが表示されてしまう問題を抑制するためには、次の手順をとってください。

  • MinGW API for MS-Windowsをダウンロードします。“w32api-x.xx-mingw32-dev.tar.gz”という形式の名前が付いたファイルです。*1
  • ダウンロードしたアーカイブファイルを、Vala(もしくはMinGW)がインストールされているディレクトリに展開します。*2
  • Valaコンパイラ“-X –mwindows”というオプションとともに呼び出します。*3
> valac -X -mwindows --pkg gtk+-2.0 hellogtk.vala

(最終更新: 2011年1月4日 13:04 /執筆: NicolasJoseph)

******************************

*1:訳者:“dev”という接尾辞のあるものです。

*2:訳者:前述の.exeを使用してValaコンパイラ環境をインストールすると、Cディレクトリ直下に“vala-x.xx.x”というディレクトリが作成されます。このディレクトリには、libincludeといったディレクトリが配置されています。MinGW API for MS-Windowsのアーカイブファイルを展開するとlibincludeといったディレクトリがあらわれるので、これらをC:\vala-x.xx.x\libC:\vala-x.xx.x\includeに上書きします。

*3:訳者: “-X –xxx”というのは“-xxx”の部分がCコンパイラに与えられるオプションであることをあらわしています。

JavaプログラマーからみたVala (4) クラス②

原典は、“Vala for Java Programmers”(2011年3月13日取得)です。

******************************

列挙型

Javaの場合: 列挙型はクラスをベースにしています。

Valaの場合: 列挙型は整数をベースにしています。メソッドは持てますが、コンストラクターやフィールドなどは持つことができません。

enum Season {
    SPRING,
    SUMMER,
    AUTUMN,
    WINTER;

    public bool is_hot () {
        return this == SUMMER;
    }
}

実行時の型判別

動的型チェック

Javaの場合: instanceof演算子を使用します。

Valaの場合: is演算子を使用します。

動的型キャスト

Javaの場合:

Foo foo = (obj instanceof Foo) ? (Foo) obj : null

Valaの場合:

Foo foo = obj as Foo

もちろん、 "(obj is Foo) ? (Foo) obj : null"も同様に有効な式です。

型情報を取得する

Javaの場合:

Class c = Foo.class;
System.out.println(c.getName());
Foo = (Foo) c.newInstance();

Valaの場合: typeof()を使用します。

Type t = typeof (Foo);
stdout.printf ("%s\n", t.name ());
Foo o = (Foo) Object.new (t);

オブジェクトの破棄

Javaの場合: 終了化子(finalizers)は、非決定論的です。

public class Foo {
    @Override
    protected void finalize() {
    }
}

Valaの場合: デストラクタ(destructors)は、決定的です。

public class Foo : Object {
    ~Foo () {
    }
}

アノテーション

Javaの場合: アノテーションは自己定義的です。

Valaの場合: アノテーションの代わりに属性(attributes)を用います。属性はコンパイラに組み込まれています。書式は、“[AttributeName (param1 = value, param2 = value)]”というかたちです。バインディング(bindings)やD-Busインターフェースのためにしばしば使用さてます。バインディングのために使用されるいちばんよく見る属性は、“[CCode (...)]”です。

プロパティ

Javaの場合: Bean規約が用いられます。getX()setX()という形式のメソッドです。

public class Person {
    private int age = 32;
    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        Person p = new Person();
        p.setAge(p.getAge() + 1);
    }
}

Valaの場合: プロパティ──get {} ・ set {}ブロック──がサポートされており、フィールドのようにアクセスできます。

public class Person : Object {
    private int _age = 32;
    public int age {
        get { return _age; }
        set { _age = value; }
    }
}

void main () {
    var p = new Person ();
    p.age++;
}

標準的な実装であれば、さらに短くできます。

public class Person : Object {
    public int age { get; set; default = 32 }
}

デリゲート、クロージャ

Javaの場合: デリゲート・パターンの実装は、匿名内部クラスを使用することになります。

public interface MyDelegateType {
    public void invoke(int a, double b);
}

public class Demo {
    private static void foo(MyDelegateType deleg) {
        deleg.invoke(32, 0.25);
    }

    public static void main(String[] args) {
        MyDelegateType deleg = new MyDelegateType () {
            public void invoke(int a, double b) {
                System.out.println("a = " + a + "; b = " + b);
            }
        };

        deleg.invoke(42, 0.75);
        foo(deleg);
    }
}

Valaの場合: Vala言語は、デリゲートとクロージャをサポートしています。

delegate void MyDelegateType (int a, double b);

void foo (MyDelegateType deleg) {
    deleg (32, 0.25);     // invoke delegate
}

void main () {
    MyDelegateType deleg = (a, b) =&gt; {
        stdout.printf ("a = %d; b = %g\n", a, b);
    };

    deleg (42, 0.75);     // invoke delegate
    foo (deleg);          // pass delegate to a method
}

クロージャは外部スコープ(それ自身が定義されたスコープ)のローカル変数を束縛する無名のメソッドです*1。クロージャはデリゲート型の変数に代入したり、デリゲート型の引数として他のメソッドに渡すことができます。

Java言語においては、匿名内部クラスにより、クロージャのまねごとができます。しかし匿名内部クラスが束縛できる外部スコープ・ローカル変数は、final宣言されたものだけです。一方、Vala言語ではクロージャはいかなる変数でも束縛できます。Java7ではクロージャのサポートが計画されています。メソッドは直接デリゲート変数に代入できます。

delegate int MyDelegateType (int a, double b);

int add (int a, int b) {
    return a + b;
}

int sub (int a, int b) {
    return a - b;
}

void main () {
    MyDelegateType deleg = add;
    int sum = deleg (2, 3);
    deleg = sub;
    int diff = deleg (8, 4);
}

このことが示すのは、メソッドは(訳者:クロージャに限らずこれまで登場してきたあらゆる種類のメソッドは)オブジェクト同様に変数に格納したり引数として渡したりできるということです。

******************************

JavaプログラマーからみたVala (5)につづく──

*1:訳者:JavaScriptの関数がまさにそれです。Java6ではfinal宣言された変数でないと外部スコープの変数にはアクセスできませんが、JavaScriptも含め他の言語の中にはこうした変数の読み書きを行えるものがあります。JavaScriptではこの方法で他のコードがアクセスできない文字通り匿名の名前空間を展開・保持するテクニックが頻繁に使用されます。