Ruby | Scala |
---|---|
|
|
|
|
Rubyではinitialize
という名前のメソッドを定義する。
Scalaにはコンストラクタを複数定義できるが、ここではデフォルトのコンストラクタについて説明する。
デフォルトのコンストラクタはクラス定義のブラケット内にそのまま記述する。
引数はクラス名の後ろのカッコ内に書く。
Ruby | Scala |
---|---|
|
|
キーワードは同じ def
引数と返り値の型を指定する。
Ruby | Scala |
---|---|
|
|
デフォルトのゲッター、セッター
Ruby | Scala |
---|---|
|
|
セッターの実装
Ruby | Scala |
---|---|
|
|
privateなフィールド _bar
を定義し、ゲッターとセッターを別途定義した。
ゲッターメソッドは公開するフィールドの名前 bar
で定義し、 セッターメソッドは フィールド名_=(引数)
という名前で定義する。
どちらも記号を名前としたメソッドを定義することで演算子を定義できる。
Ruby | Scala |
---|---|
|
|
Rubyでメソッドとして再定義可能な演算子
| ^ & <=> == === =~ > >= < <= << >>
+ - * / % ** ~ +@ -@ [] []= ` ! != !~
Scalaで再定義できない予約語、キーワード
(FAQ How do I find what some symbol means or does?より引用)
(これ以外の記号、英数字は使えるらしい)
// Keywords
<- // Used on for-comprehensions, to separate pattern from generator
=> // Used for function types, function literals and import renaming
// Reserved
( ) // Delimit expressions and parameters
[ ] // Delimit type parameters
{ } // Delimit blocks
. // Method call and path separator
// /* */ // Comments
# // Used in type notations
: // Type ascription or context bounds
<: >: <% // Upper, lower and view bounds
" """ // Strings
' // Indicate symbols and characters
@ // Annotations and variable binding on pattern matching
` // Denote constant or enable arbitrary identifiers
, // Parameter separator
; // Statement separator
_* // vararg expansion
_ // Many different meanings
種類 | Ruby | Scala | 備考 |
---|---|---|---|
引数のデフォルト値 |
|
|
|
可変長引数 |
|
|
Rubyでは定義するときも呼び出すときも `*` という記号を前につける。 Scalaでは定義するときは `*` を後ろにつけ、呼び出すときは `:_*` という記号を後ろにつける。 |
キーワード引数 |
|
|
Scalaでは普通に定義したメソッドに名前付き引数を渡して呼び出すことができる。 |
Ruby | Scala |
---|---|
|
|
クラスと同名のシングルトン(コンパニオンオブジェクト)を定義し、その中にメソッドを記述する。
Ruby | Scala |
---|---|
|
|
RubyとScalaの可視性に関する機能は厳密には一致しない。
たとえばScalaのprivate
はRubyのprotected
と違ってサブクラスからはアクセスできない。
Scalaではトレイトを継承することでmixinを行う。
barというメソッドを持つトレイトFooを定義し、Bazで継承する。
Ruby | Scala |
---|---|
|
|
ひとつのクラスが複数のtraitをmixinする場合、二つ目以降は with というキーワードを使う。
class Foo extends Bar with Baz with Hoge {
}
Rubyではモジュールを名前空間として使うことができる。
Scalaではパッケージのほか、シングルトンオブジェクトも名前空間として使うことができる。
Ruby | Scala |
---|---|
|
|
TODO: import
について書く
既存のクラスにメソッドを追加する場合、implicit class
という仕組みを使う。
オブジェクトのスコープを使って、拡張の影響範囲をコントロールすることができる。
RubyのRefinementsに相当する。
Ruby | Scala |
---|---|
|
|
Intに対して.plus()
メソッドを呼ぶコードは、コンパイラによって
new IntExtention(1).plus(2)
という式に変換されてコンパイルされる。インスタンス生成のオーバーヘッドを避けたい場合は値クラスを併用する。
http://docs.scala-lang.org/ja/overviews/core/value-classes.html#section-1
下記のような既存のメソッドの挙動を修正する「モンキーパッチ」は行うことができない。
Structural Types(構造的部分型)をつかってダックタイピングを実現することができる。
Ruby | Scala |
---|---|
|
|
test関数のahiru引数をつけられた型注釈 { def quack: Unit }
が構造的部分型で、
「() => Unit
という型を持つメソッドquack
を実装したオブジェクト」
という意味をもつ。
これにより、quackメソッドを持たないオブジェクトをtest関数に渡すと、コンパイル時にエラーとなる。
JVMの仕様上の制限から、test()関数の中で ahiru.quack を呼び出すときリフレクションが使われるため、パフォーマンスはよくないとされている。
Scalaではクラスの宣言時でなくインスタンスの生成時にtraitを(静的に)ミックスインすることができるので、抽象メソッドをもつtraitをインターフェースとして宣言することで同様のことができる。
trait DuckLike {
def quack: Unit
}
def test2(ahiru: DuckLike): Unit = ahiru.quack
val duck: DuckLike = new Duck() with DuckLike
test2(duck) // => ガーガー
val dakku: DuckLike = new Cat() with DuckLike
test2(dakku) // => にゃーん
val car: DuckLike = new Car() with DuckLike // => コンパイルエラー
<console>:9: error: object creation impossible, since method quack in trait DuckLike of type => Unit is not defined
Rubyの method_missing にあたるものとして Dynamic トレイトがある。
Ruby | Scala |
---|---|
|
|
実行時のメソッド呼び出しにフックをかけるmethod_missingと違い、Dynamicトレイトはコンパイル時のコード変換によって実現されている。
Dynamicトレイトを継承したクラスに対して、未定義のメソッド呼び出しが記述されていると、コンパイラは呼び出しの形式に応じて xxxDynamic() メソッドの呼び出しに変換する。
(つまりコンパイラはDynamicトレイトを特別扱いしている。)
selectDynamic() などのメソッド定義では、Scalaの通常のメソッドと同じように引数や返り値の型を明記しなければならない。
返り値の型を個々の呼び出しで使い分けたいときは、型パラメータを使ったジェネリックなメソッドとして実装する。
class Foo extends Dynamic {
def selectDynamic[A](name: String): A = {
val result = doSomething // 必要な処理を書く
result.asInstanceOf[A]
}
}
val foo = new Foo
foo.hoge[Int] // hogeに関する処理がIntを返す場合
val i: Int = foo.hoge // 変数にIntの型がついているので型パラメータを省略できる
asInstanceOf
によるキャストのほかに、型クラスを使う方法もある。
https://stackoverflow.com/questions/15799811/how-does-type-dynamic-work-and-how-to-use-it