Scala School (3)

終わり、と言いつつ朝練続きをスキマ時間で。読んでるのは以下です。

Classes

最初に出てくるのは色々な意味で普通のクラス定義ですね。とりあえず例示されてるソレを REPL に食わせてみます。

scala> class Calculator {
     | val brand: String = "HP"
     | def add(m: Int, n: Int): Int = m + n
     | }
defined class Calculator

scala> val calc = new Calculator
calc: Calculator = Calculator@ff105de

scala> calc.add(1, 2)
res4: Int = 3

scala> calc.brand
res5: String = HP

属性の定義は val を使わないと駄目なのだろうか。ちょい謎。

Constructor

以下な例が出てます。

class Calculator(brand: String) {
  /**
   * A constructor.
   */
  val color: String = if (brand == "TI") {
    "blue"
  } else if (brand == "HP") {
    "black"
  } else {
    "white"
  }

  // An instance method.
  def add(m: Int, n: Int): Int = m + n
}

これってクラス定義に引数付ければ先頭部分がコンストラクタな記述ができます、ってことなのかなぁ。あるいは最初に例示したソレも変数の定義部分は実はコンストラクタな記述だったりするのかどうか。
コメントの書き方も二種類あります、とか出てますね。

Expressions

ここ、短いですが興味深い項。Scala は expression-oriented だ、とのこと。コンストラクタにおいて if の式の結果を color に代入しているのが分かります。

val color: String = if (brand == "TI") {
    "blue"
  } else if (brand == "HP") {
    "black"
  } else {
    "white"
  }

こういった記述をすっと受け入れることができるのは Scheme のお陰ですね。

Aside: Functions vs Methods

実はこのあたりから現実トウヒベースなナニになっていたりして。
そりゃ良いとしてこの項も興味深いですね。function と method は違うモノ、なのかどうか。例えば以下な定義があるとして

scala> class C {
     |   var acc = 0
     |   def minc = { acc += 1 }
     |   val finc = { () => acc += 1 }
     | }
defined class C

def で定義 (?) されてる minc はメソッドです。対して手続きオブジェクトが格納されてる finc はあくまでも属性なのかどうなのか。そして例示されているソレを実行してみます。

scala> val c = new C
c: C = C@5a5e3fb4

scala> c.minc

scala> c.acc
res1: Int = 1

scala> c.finc
res2: () => Unit = <function0>

scala> c.finc()

scala> c.acc
res4: Int = 2

メソドは括弧無しでも呼びだせる、という理解では多分ダウト。でも解説はちょっと分かりづらい。とりあえずこんなもの、ってカンジで流します。

Inheritance

次の項は継承ですね。先に出てきた Calculator を継承した ScientificCalculator の定義が例示されてます。つうか Type alias って何でしょ。継承元とさほど違いがないのであれば使いなさい、ってことなのかな。
このあたりの記述かな。

Don’t use subclassing when an alias will do.


trait SocketFactory extends (SocketAddress => Socket)

a SocketFactory is a function that produces a Socket. Using a type alias


type SocketFactory = SocketAddress => Socket

is better.

ちょっとこのあたりはまだスルーの方向で。

Overloading methods

オーバーロードも普通に可能。あ、override ではなくて overload なんですね。log というメソドの引数の型とかその数が異なっています。
あら、ScientificCaluculator の log は戻り型の記述が無いですが自動で double とかになるのかな。ちょい確認を。
まずおおもとを定義。

scala> class Calculator(brand: String) {
     |   /**
     |    * A constructor.
     |    */
     |   val color: String = if (brand == "TI") {
     |     "blue"
     |   } else if (brand == "HP") {
     |     "black"
     |   } else {
     |     "white"
     |   }
     | 
     |   // An instance method.
     |   def add(m: Int, n: Int): Int = m + n
     | }
defined class Calculator

で、サブクラスを定義。

scala> class ScientificCalculator(brand: String) extends Calculator(brand) {
     |   def log(m: Double, base: Double) = math.log(m) / math.log(base)
     | }
defined class ScientificCalculator

そのまた子供も定義。

scala> class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
     |   def log(m: Int): Double = log(m, math.exp(1))
     | }
defined class EvenMoreScientificCalculator

で、どうするんだっけ。オブジェクトを作るのか。

scala> var obj = new EvenMoreScientificCalculator("HP")
obj: EvenMoreScientificCalculator = EvenMoreScientificCalculator@3a0de57d

呼び出してみます。

scala> obj.log(1.5, 1.5)
res5: Double = 1.0

double が戻ってきますね。うーん。トップレベルで同じ定義をしてみるか。

scala> def log(m: Double, base: Double) = math.log(m) / math.log(base)
log: (m: Double, base: Double)Double

あー、戻り Double になってますね。引数を算術演算してそれを戻しているのであれば戻り値の型は類推可能なのかどうなのか。

Abstract Classes

抽象クラス、抽象メソドも以下なカンジで定義できる模様。

abstract class Shape {
  def getArea():Int    // subclass should define this
}

このクラスのオブジェクトは作れない旨も書かれてますね。

Traits

これ、何なのか。以下な記述があります。

traits are collections of fields and behaviors that you extend or mixin to your classes.

Scala School - Basics より引用
つうか_いつ抽象クラスの代りに trait 使いたい?_とかってこちらに聞かれても困るぬ。とは言えなんとなくなガイドライン的記述もありました。

  • 複数の trais を extend できる模様。クラスは Java と同様に単一継承みたいです。
  • コンスラクタのパラメータが必要であれば抽象クラスを使えとのこと

いくつかのポインタも列挙されてますね。stackoverflow とか Programming in Scala とか。