空飛ぶ羊

勉強したり登壇したり勉強会参加したり

学習メモ: クラス (2/2) #dwango #Scala

この記事について

ドワンゴScala研修テキストを学習したときのメモです。 okoysm.hatenablog.jp

前回はこちら okoysm.hatenablog.jp

DOING

クラス

メソッド定義

メソッド本体が1つの式だけからなる場合

基本形(実態)。

(private[this/package名]/protected[package名]) def methodName(parameter1: Type1, parameter2: Type2, ...): ReturnType = ???

一般的な定義

  • ={}を使ったスタイルのほうが{}内に複数の式を並べて書けることを利用した派生形。
  • こっちのパターンのほうが使うことは多い
(private[this/package名]/protected[package名]) def methodName(parameter1: Type1, parameter2: Type2, ...): ReturnType = {
  ???
}

返り値の型は明記しよう

  • 省略しても特別な場合以外は型推論してくれるけれども、読みやすさのために明記する習慣をつけよう
    • 個人的にはこれがいいから静的型付け言語好き

アクセス権

  • アクセス権は3種類 (private, protected, public)
    • private:そのクラス内だけからのみアクセス可能
    • protected:派生クラスからのみアクセス可能
    • public:どこからもアクセス可能
    • 何も付けない:publicになる((ここだけJavaと違う。Javaの場合protected〜private程度のアクセス権だった))

Scala独特のアクセス権まわりの書き方

  • private[this]:同じオブジェクトからのみアクセスが可能
  • private[package名]:同一パッケージに所属しているものからのみアクセスが可能
  • protected[package名]:派生クラス+同じパッケージに所属しているもの全てからアクセスできるようになる

メソッドを実装してみる

メソッド名に+を指定できるのが衝撃だった

scala> class Point(val x: Int, val y: Int) {
     |   def +(p:Point): Point = {
     |     new Point(x + p.x, y + p.y)
     |   }
     |   override def toString(): String = "(" + x + ", " + y + ")"
     | }
defined class Point

scala> val p1 = new Point(1, 1)
p1: Point = (1, 1)

scala> val p2 = new Point(2, 2)
p2: Point = (2, 2)

scala> p1+p2
res0: Point = (3, 3)

複数の引数リストを持つメソッド

メソッドは複数の引数リストを持つように定義することができる

def methodName(parameter11: Type11, parameter12: Type12, ...)(...)(parameterN1: TypeN1, ..., parameterNM: TypeNM): RerurnType = ???

用途

実装してみる

複数の引数リストを持つ加算メソッドを定義してみる。

  • 複数の引数リストを持つメソッドはobj.m(x)(y)の形式で呼びだす
  • 一番下のように、最初の引数だけを適用して新しい関数を作る(部分適用)こともできる
scala> class Adder {
     |   def add(x: Int)(y: Int): Int = x + y
     | }
defined class Adder

scala> val adder = new Adder()
adder: Adder = Adder@5132ea8c

scala> adder.add(2)(3)
res2: Int = 5

scala> adder.add(2) _
res3: Int => Int = $$Lambda$1791/569849097@1ceeb7d7

疑問点

  • 複数の引数リストを持つメソッドで、最初の引数だけを適用して新しい関数を作る(部分適用)の使い所はどこ?

フィールド定義

定義

(private[this/package名]/protected[package名]) (val/var) fieldName: Type = Expression

val/var

  • val: 変更不能なフィールドになる
  • var: 変更可能なフィールドになる

アクセス権

  • private : そのクラス内だけからのみ
  • private[this] : 同じオブジェクトのみ
  • private[package名] : 同一パッケージからのみ
  • protected : そのクラスの派生クラスからのみ
  • protected[package名] : 派生クラスと同一パッケージに所属しているもの全て
  • なにもつけない:public

private[this]は若干高速

  • JVMレベルでのフィールドへの直接アクセスになるため、若干高速
  • 細かいレベルでのパフォーマンスチューニングをする場合は意識すると良い

継承

クラスのもう一つの機能が継承

継承の目的

Javaの継承の目的とあんまり変わらん

  1. 継承によりスーパークラスの実装をサブクラスでも使うことで実装を再利用すること
  2. 複数のサブクラスが共通のスーパークラスのインタフェースを継承することで処理を共通化すること

実装の継承におけるJavaとの違い

ScalaはTrait(次々回)を使うことで複数の実装の継承ができる

実装の継承には複数の継承によりメソッドやフィールドの名前が衝突する場合の振舞いなどに問題があることが知られており、Javaでは実装継承が1つだけに限定されています。 Java 8ではインタフェースにデフォルトの実装を持たせられるようになりましたが、変数は持たせられないなどの制約があります。 Scalaではトレイトという仕組みで複数の実装の継承を実現していますが、トレイトについては別の節で説明します。

通常のクラスの継承の構文

Javaと同じくextends書く

class SubClass(....) extends SuperClass {
  ....
}

メソッドをoverrideするとき

JavaアノテーションJavadocで記載していたが、Scalaでは明示的に書く

scala> class APrinter() {
     |   def print(): Unit = {
     |     println("A")
     |   }
     | }
defined class APrinter

scala> class BPrinter extends APrinter {
     |   override def print(): Unit = {
     |     println("B")
     |   }
     | }
defined class BPrinter

scala> new APrinter().print
A

scala> new BPrinter().print
B

この章での疑問点

  • コンストラクタの引数をそのまま公開したくない場合などあるのだろうか(Javaでいうprivateな変数にしてgetter/setterで取らせたいみたいなやつかな)
  • 複数の引数リストを持つメソッドで、最初の引数だけを適用して新しい関数を作る(部分適用)の使い所はどこ?

進捗

DONE

TODO

  • オブジェクト
  • トレイト
  • 型パラメータと変位指定
  • 関数
  • Scalaのコレクションライブラリ
  • ケースクラスとパターンマッチング
  • エラー処理
  • Implicit
  • 型クラスの紹介
  • FutureとPromise
  • テスト
  • Javaとの相互運用
  • S99の案内
  • トレイトの応用編:依存性の注入によるリファクタリング