この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
Rubyで書くあれはScalaだとどう書くの?シリーズ。 今回はクラスとオブジェクト指向について。
クラス
クラスの宣言と継承
Ruby
class Foo < Bar
end
Scala
class Foo extends Bar {
}
コンストラクタ
Rubyではinitialize
という名前のメソッドを定義する。
class Foo
def def initialize(bar)
# new した時点でここが実行される
end
end
Scalaにはコンストラクタを複数定義できるが、ここではデフォルトのコンストラクタについて説明する。
デフォルトのコンストラクタはクラス定義のブラケット内にそのまま記述できる。
引数はクラス名の後ろのカッコ内に書く。
class Foo(bar: Int) {
// new した時点でここが実行される
}
モジュール
Ruby
module Foo
def bar
end
end
Scala
trait Foo {
def bar: Unit = {
}
}
barというメソッドを持つtrait Fooを定義することができる。
Mixin
Scalaではtraitを継承することでmixinを行う。
Ruby
module Bar
end
class Foo
include Bar
end
Scala
trait Bar {
}
class Foo extends Bar {
}
ひとつのクラスが複数のtraitをmixinする場合、二つ目以降は with というキーワードを使う。
Scala
class Foo extends Bar with Baz with Hoge {
}
TODO: prepend
やusing
についても調べて書く
名前空間
Rubyではモジュールを名前空間として使うことができる。
Ruby
module Foo
module Bar
class Baz
end
end
end
baz = Foo::Bar::Baz.new
Scalaではパッケージのほか、シングルトンオブジェクトも名前空間として使うことができる。
Scala
package foo
object Bar {
class Baz {
}
}
val baz = new foo.Bar.Baz()
TODO: import
について書く
メソッド
キーワードは同じ def
引数と返り値の型を指定する。
Ruby
class Foo
def bar(a, b)
a + b
end
end
Scala
class Foo {
def bar(a: Int, b: Int): Int = {
a + b
}
}
プロパティとアクセサ
デフォルトのゲッター、セッター
Ruby
class Foo
attr_accessor :bar, :baz
def initialize(bar, baz)
@bar = bar
@baz = baz
end
end
foo = Foo.new(1, "abc")
foo.bar # => 1
foo.baz # => "abc"
Scala
class Foo(var bar: Int, var baz: String) {
}
val foo = new Foo(1, "abc")
foo.bar // => 1
foo.baz // => "abc"
セッターの実装
Ruby
class Foo
attr_reader :bar
def bar=(i)
@bar = i
end
end
foo = Foo.new
foo.bar = 1
foo.bar # => 1
Scala
class Foo {
private var _bar: Int = _
def bar = _bar
def bar_=(i: Int): Unit = this._bar = i
}
val foo = new Foo()
foo.bar = 1
foo.bar // => 1
Scalaのセッターメソッドは def 変数名_=(引数): Unit = ???
という形で定義する。
演算子メソッド
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
どちらも記号を名前としたメソッドを定義することで演算子を定義できる。
class Foo
def +(other)
"add method"
end
end
foo1 = Foo.new
foo2 = Foo.new
foo1 + foo2 # "add method"
class Foo {
def +(other: Foo): String = "add method"
}
val foo1 = new Foo
val foo2 = new Foo
foo1 + foo2 // "add method"
引数のデフォルト値
Ruby
class Foo
def bar(baz = 1)
baz
end
end
foo = Foo.new
foo.bar() # => 1
foo.bar(2) # => 2
Scala
class Foo {
def bar(baz: Int = 1): Int = {
baz
}
}
val foo = new Foo()
foo.bar() // => 1
foo.bar(2) // => 2
可変長引数
Rubyでは定義するときも呼び出すときも *
という記号をまえにつける。
class Foo
def bar(baz, *hoge)
# 2つめ以降の引数はhogeに配列として入る
end
end
foo = Foo.new
baz = 1
hoge = ["foo", "bar"]
foo.bar(baz, *hoge)
Scalaでは定義するときは *
をうしろにつけ、呼び出すときは :_*
という記号をうしろにつける。
class Foo {
def bar(baz: Int, bar: String*): Unit = {
// 2つめ以降の引数は bar にSeqとして入る
}
}
val foo = new Foo
val baz = 1
val hoge = Seq("foo", "bar")
foo.bar(baz, hoge:_*)
※読みやすいレイアウトでまとめましたサイトを作りましたので、こちらもご参照ください。
http://kkismd.github.io/rb2scala/