S-JIS[2011-01-07] 変更履歴
Scalaでは、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 =
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 |
関数型を定義してみる例。 |
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インスタンスはキャッシュされて使い回される)