S-JIS[2011-01-07] 変更履歴

Scala type(別名)

Scalaでは、typeによって型に別名を付けることが出来る。


type

C言語C++のtypedefのように、)Scalaでは「type」を使って型(クラストレイト関数型等)に別名(alias)を付けられる。

type 別名 = 本来の型
Scalaの例 C言語相当 備考
scala> type T = Int
defined type alias T
val n:T = 123
typedef int T;
 
const T n = 123;
別名を付ける例と、それを使う例。
型パラメーターも込みで別名を付けられる。
type S = String
type A = Array[Char]
val s :S = "abc"
val cs:A = "abc".toCharArray
typedef char A[];
 
const A cs = "abc";
abstract class P {
  type T //抽象タイプT

  // Tを使った変数・メソッド定義
  var t:T = _
  def set(a:T) = { t = a }
  def get():T  =   t
}
class S extends P {
  type T = String
}

val s = new S
s.set("abc")
s.get()
class I extends P {
  type T = Int
}

val n = new I
n.set(456)
n.get()
  抽象タイプを宣言することが出来る。
(「=」以降を書かない)

あるクラストレイト)内で別名の名前だけ定義し、
実際に何の型であるかは子クラスで定義する。

(この使い方の場合、ジェネリクスとの使い分けの違いは何なんだろうな?
class P[T] {
  var t:T = _
  def set(a:T) = { t = a }
  def get():T  =   t
}
class S extends P[String]

抽象タイプは、表に出てこない、内部だけで使用する型を抽象化したい場合に使うのかな?)

class A1
class A2 extends A1
class A3 extends A2
trait P {
  type T <: A2
}

× class C extends P { type T = Any }
× class C extends P { type T = A1 }
○ class C extends P { type T = A2 }
○ class C extends P { type T = A3 }
○ class C extends P { type T = Nothing }
trait P {
  type T >: A2
}

○ class C extends P { type T = Any }
○ class C extends P { type T = A1 }
○ class C extends P { type T = A2 }
× class C extends P { type T = A3 }
× class C extends P { type T = Nothing }
trait P {
  type T >: A3 <: A1
}

× class C extends P { type T = Any }
○ class C extends P { type T = A1 }
○ class C extends P { type T = A2 }
○ class C extends P { type T = A3 }
× class C extends P { type T = Nothing }
  抽象タイプでは、ジェネリクスと同様に
上限境界下限境界を指定できる。
(「<%」は使えないようだ)
type T = { def zzz():Unit }
// Hogeクラスを継承し、かつzzz()を持つ
type T = Hoge { def zzz():Unit }
// HogeクラスとFooトレイトとzzz()を持つ
type T = Hoge with Foo {
  def zzz():Unit
}
  型名だけなく、「どういうメソッドを含んでいればよいか」という定義だけを指定することも出来る。
Structural Subtyping(構造的部分型)
scala> type F = (Int) => Unit
defined type alias F

scala> val f:F = (n:Int) => println("f"+n)
f: (Int) => Unit = <function1>

scala> f(123)
f123
scala> type F = (Any) => Unit
defined type alias F

scala> val f:F = println
f: (Any) => Unit = <function1>

scala> f(123)
123
  関数型を定義してみる例。

Structural Subtyping(構造的部分型)

typeによって別名を付けるとき、(型名ではなく)抽象クラス定義そのものを指定することが出来る。

// 例:zzz()というメソッドを持つ型
type Z = { def zzz():Unit }

すると、継承関係の無いクラスでも、その抽象クラス定義と同じ形を含んでいれば(同じメソッドを持ってさえいれば)、同じ型として扱える。
これを構造的部分型(Structural Subtyping)と呼ぶらしい。

// クラスAとBには継承関係は無い
class A {
  def zzz() = println("aaa")
}

class B {
  def zzz() = println("bbb")
}
scala> var z:Z = _
z: Z = null

scala> z = new A()
z: Z = A@c21a68

scala> z.zzz()
aaa

scala> z = new B()
z: Z = B@708aff

scala> z.zzz()
bbb
scala> val list = List[Z](new A, new B)
list: List[Z] = List(A@d942d9, B@1b3cc96)

scala> for(z <- list) z.zzz()
aaa
bbb

Javaならzzz()というメソッドを持つインターフェースを定義してクラスAとBに実装(implements)させるところだが、
構造的部分型ではそういうことをせず、同じ形式(構造)を持っていれば同じ扱いが出来る、という考えのようだ。


なお、構造的部分型では、typeで別名を定義しなくても、直接変数の型に指定することが出来る。

scala> var z: { def zzz():Unit } = _
z: AnyRef{def zzz(): Unit} = null

scala> class A { def zzz() = println("aaa") }
defined class A

scala> z = new A()
z: AnyRef{def zzz(): Unit} = A@d1817d

scala> z.zzz()
aaa

ちなみに、構造的部分型を使ったメソッド呼び出しがコンパイルされてJavaになったときには、
リフレクション(getClass()・getMethod()およびinvoke())によって呼び出される形式になるようだ。
(getMethod()によって取得されたMethodインスタンスはキャッシュされて使い回される)


Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま