2011年3月13日日曜日

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

最近なにかの拍子にVala言語に興味を持ってしまいました。Web上のドキュメントは限られているものの、Java言語との比較など平易なドキュメントもありましたので、学習をかねて稚拙な翻訳を載せていこうと思います。

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

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

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

継承

Javaの場合: extendsimplementsを使用します。
public class Demo extends Foo implements Bar {
    public Demo() {
        super();
    }
}

Valaの場合: コロンとそれに続くカンマ区切りリストであらわします。このリストには親クラスもインターフェースもともに含まれます。
public class Demo : Foo, Bar {
    public Demo () {
        base ();
    }
}

Vala言語では親クラス(super)を基本クラス(base)と呼びます。

オブジェクトベースクラス

Javaの場合: すべてのクラスはObject(java.lang.Object)から暗黙的に継承します。
public class Foo {
    // ...
}

Valaの場合: Object(Glib.Object)からの暗黙的な継承は行われません。
public class Foo : Object {
    // ...
}

Glib.Objectからの継承を行わないとどうなるのでしょうか? このような場合、そのクラスは若干軽量になる一方、プロパティ変更通知(property change notifications)などのいくつかの機能を欠いたものになります。そしてこのクラスは共通基本クラスを持たないことになります。たいていの場合、Glib.Objectからの継承は必要となるでしょう。

メソッド多重定義

Javaの場合: 以下の例のようにメソッドの多重定義(overloading)が行えます。
public class Demo {

    public void draw(String text) { }

    public void draw(Shape shape) { }


    /* メソッドのオーバーロード、
      そしてより引数の数の少ないメソッドによる、
        引数の数の多いそれの利用*/

    void f(int x, String s, double z) { }

    void f(int x, String s) {
        f(x, s, 0.5);
    }

    void f(int x) {
        f(x, "hello");
    }
}

Valaの場合: メソッドの多重定義はできません。別名を付けるか、引数にデフォルト値を指定することで代わりとします。
public class Demo : Object {

    public void draw_text (string text) {
    }

    public void draw_shape (Shape shape) {
    }

    /* 引数にデフォルト値が指定されたメソッド */
    void f (int x, string s = "hello", double z = 0.5) {
    }
}

Vala言語がメソッド多重定義の機能をサポートしないのは、Valaのライブラリが、C言語コードからアクセス可能であるよう意図して記述されているからです(意訳)。

コンストラクタ多重定義

Javaの場合: コンストラクタの多重定義ができます。
public class Foo {
    public Foo() { }
    public Foo(int foo) { }
    public Foo(String bar) { }
}
new Foo();
new Foo(42);
new Foo("hello");

Valaの場合: 多重定義ではなく、名前付きコンストラクタ(named constructors)を使用します。
public class Foo : Object {
    public Foo () { }
    public Foo.with_foo (int foo) { }
    public Foo.from_bar (string bar) { }
}
new Foo ();
new Foo.with_foo (42);
new Foo.from_bar ("hello");

コンストラクタ連鎖

Javaの場合: “this()”記法で実現します。
class Foo {
    public Foo() {
        this("bar");
    }

    public Foo(string bar) {
    }
}

Valaの場合: “this()”記法もしくは“this.コンストラクタ名 ()”記法で実現します。
class Foo : Object {
    public Foo () {
        this.with_bar ("bar");
    }

    public Foo.with_bar (string bar) {
    }
}

オーバーライド

Javaの場合: すべてのメソッドはデフォルトで仮想メソッド(C#で導入された概念。オーバーライド可能なメソッド)です。オーバーライドを阻止するにはfinal修飾子を使用します。
public class Super {
    public int myMethod(int x, int y) { }
    public final void anotherMethod() { }
}

public class Sub extends Super {
    @Override
    public int myMethod(int x, int y) {
        super.myMethod(x, y);
        // ...
    }
}

Valaの場合: すべてのメソッドは、デフォルトで非仮想メソッド(オーバーライド不可能なメソッド)です。オーバーライドを可能にするには明示的にvirtual修飾子を使用する必要があります。 他方、Vala言語には@Overrideアノテーションの代わりにoverride修飾子があり、この修飾子の使用は任意ではありません(強制です)。
public class Super : Object {
    public virtual int my_method (int x, int y) { }
    public void another_method () { }
}

public class Sub : Super {
    public override int my_method (int x, int y) {
        base.my_method (x, y);
        // ...
    }
}

アクセス修飾子


Java
Vala
public
public
protected
protected
package-private (デフォルト)
internal
private
private (デフォルト)

クラスメンバーのアクセスはデフォルトでprivateですが、publicアクセスのメンバーと対称的にさせるために、明示的にprivate修飾子を使用することもできます。

インターフェース

Javaの場合: インターフェースのメソッドは暗黙のうちに抽象メソッドです。
public interface Foo {
    public void foo(int i);
    public int bar(String s, double d);
}

Valaの場合: abstract修飾子は明示的に使用する必要があります。
public interface Foo {
    public abstract void foo (int i);
    public abstract int bar (string s, double d);
}

なぜでしょう? それはVala言語におけるインターフェースが非抽象メソッド(実装をともなうメソッド)をメンバーとすることができるからです。つまりVala言語のインターフェースは、いわゆるミックスイン(mixins。限定的な多重継承)を実現するために利用できるのです。
Vala言語におけるインターフェースは、──例えばファクトリーメソッドのような──staticメソッドを持つこともできます。

Javaの場合: インターフェースは継承可能です。
public interface IfaceA {
    public void methodA();
}

public interface IfaceB extends IfaceA {
    public void methodB();
}

public class Demo implements IfaceB {
    public void methodA() { }
    public void methodB() { }
}

Valaの場合: インターフェースは必要条件として働きます。
interface IfaceA : Object {
    public abstract void method_a ();
}

interface IfaceB : Object, IfaceA {
    public abstract void method_b ();
}

class Demo : Object, IfaceA, IfaceB {
    public void method_a () { }
    public void method_b () { }
}

Valaでは、インターフェースは他のインターフェースから継承できません。しかし他のインターフェースを必要条件として宣言できます。これによりおおよそ同じことを実現できます。インターフェースは、インターフェースだけでなくクラスも前提条件として宣言することができます。これにより、あるインターフェース(を継承したあるクラス)のインスタンスが、Glib.Objectのサブクラスでもあることを保証することができます。この事実──インターフェースは他のインターフェースのメンバーを継承できない──は、ほとんど技術的なちがいでしかありません。実際のところVala言語のインターフェースのシステムは、Javaにおけるそれと同じように働きます。ただ、Valaにおいてはクラス継承を必要条件とするという機能もある、ということです。

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

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

0 件のコメント: