Revision 9 as of 2011-04-30 00:32:40

Clear message
Locked History Actions

scala/typeSystem

Scalaの型システム

単純な型パラメータ

Javaでは以下のように書ける。

class Animal { }
class Fish extends Animal { }
class Zoo<T> {
    public void add(T animal) {
    }
}
...
        Zoo<Animal>zoo = new Zoo<Animal>();
        zoo.add(new Fish());
...

これと等価な(たぶん)Scalaコードは以下。

class Animal
class Fish extends Animal;
class Zoo[T] {
  def add(a:T) { }
}

val zoo = new Zoo[Animal];
zoo add new Fish

上限型境界

上のコードのままだと、Animal以外のZooの作成が可能になってしまう。

  Zoo<Integer>zooInt = new Zoo<Integer>(); // 可能だが意味不明

また、そもそもこのままではZooクラスの中でAnimalのメソッドを呼び出せない。Tはどんな型でも可能だからである。 Zooの中でAnimalのメソッドを(キャスト無しに)呼び出すためには、TがAnimalクラスあるいはそのサブクラスであることを明示する必要がある。

そこで上限型境界指定を行う。Javaでは以下のようなものだった

class Zoo<T extends Animal> {
...
}
Zoo<Fish>fishZoo = new Zoo<Fish>(); // OK
Zoo<Integer>intZoo = new Zoo<Integer>(); // エラー

Scalaでは以下になる

class Zoo[T <: Animal] {
...
}
....
val fishZoo = new Zoo[Fish]; // OK
val intZoo = new Zoo[Int]; // エラー

下限型境界

上限型境界と同様に、下限型境界というものもある。Scalaでは

class Zoo[T >: Animal] {

などと記述するが、Javaにはこの機能がない。つまり、

class Zoo<T super Animal> {

と書くことはできない。後述する変位指定ではJavaも

<? super Animal>

などと書けるのだが(だったかな?)、下限型境界指定は存在しない。

しかし、そもそも下限型境界の使いどころが不明なのでこれについては省略。

変位指定の書き方

「変位」とは何か、JavaとScalaのその違いは何かはおいておき、ここではその書き方のみに注目してみる。

Javaの変位指定

Javaでは「その型を使う時に」「? extends Animal」などと記述して変位を指定する。 たとえば、

class Animal { }
class Fish extends Animal { }
class Zoo<T extends Animal> {
...
}
...
        Zoo<Fish>fishZoo = new Zoo<Fish>();
        Zoo<Animal>animalZoo1 = fishZoo; // エラー
        Zoo<? extends Animal>animalZoo2 = fishZoo; // ※OK
...

上の例でfishZooはanimalZoo1に代入することはできない(コンパイルエラー)。animalZoo2では、<? extends Animal>として変位を指定している。

Scalaの変位指定

ScalaではJavaのやり方とはまったく異なる。Javaでは上のように、「呼び出しサイト」で定義するが、Scalaでは「宣言サイト」で定義する(これらの言葉は「プログラミングScalaから拝借)。

以下はだめだが、

class Animal
class Fish extends Animal;
class Zoo[T <: Animal] {
}
val fishZoo = new Zoo[Fish];
val animalZoo: Zoo[Animal] = fishZoo;

以下はOK

class Animal
class Fish extends Animal;
class Zoo[+T <: Animal] { // Tの前に+をつけただけ
}
val fishZoo = new Zoo[Fish];
val animalZoo: Zoo[Animal] = fishZoo;