Scala Language
クラスとオブジェクト
サーチ…
構文
-
class MyClass{} // curly braces are optional here as class body is empty
-
class MyClassWithMethod {def method: MyClass = ???}
-
new MyClass() //Instantiate
-
object MyObject // Singleton object
-
class MyClassWithGenericParameters[V1, V2](vl: V1, i: Int, v2: V2)
-
class MyClassWithImplicitFieldCreation[V1](val v1: V1, val i: Int)
-
new MyClassWithGenericParameters(2.3, 4, 5)
または異なるタイプのnew MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
-
class MyClassWithProtectedConstructor protected[my.pack.age](s: String)
クラスインスタンスのインスタンス化
Scalaのクラスは、クラスインスタンスの「青写真」です。インスタンスには、そのクラスで定義されている状態と動作が含まれます。クラスを宣言するには:
class MyClass{} // curly braces are optional here as class body is empty
new
キーワードを使用してインスタンスをインスタンス化することができます。
var instance = new MyClass()
または:
var instance = new MyClass
Scalaでは、引数のないコンストラクタを持つクラスからオブジェクトを作成するためのカッコはオプションです。クラスコンストラクタが引数を取る場合:
class MyClass(arg : Int) // Class definition
var instance = new MyClass(2) // Instance instantiation
instance.arg // not allowed
ここでMyClass
は、内部でのみ使用できるInt
引数が1つ必要です。 arg
は、フィールドとして宣言されていない限り、 MyClass
外部からアクセスすることはできません:
class MyClass(arg : Int){
val prop = arg // Class field declaration
}
var obj = new MyClass(2)
obj.prop // legal statement
あるいは、コンストラクタでpublicとして宣言することもできます。
class MyClass(val arg : Int) // Class definition with arg declared public
var instance = new MyClass(2) // Instance instantiation
instance.arg //arg is now visible to clients
パラメータなしでクラスをインスタンス化する:{} vs()
コンストラクタ引数を持たないMyClassクラスがあるとしましょう:
class MyClass
Scalaでは、以下の構文を使用してインスタンス化できます。
val obj = new MyClass()
あるいは単に次のように書くことができます:
val obj = new MyClass
しかし、注意を払わなければ、オプションのかっこで予期しない動作が生じることがあります。別のスレッドで実行するタスクを作成したいとします。以下はサンプルコードです:
val newThread = new Thread { new Runnable {
override def run(): Unit = {
// perform task
println("Performing task.")
}
}
}
newThread.start // prints no output
このサンプルコードを実行するとPerforming task.
が印刷されると考えられPerforming task.
しかし、驚いたことに、それは何も印刷されません。ここで何が起こっているか見てみましょう。より詳しくお知りになりたい場合は、 new Thread
直後に中括弧{}
を使用しています。 Thread
を拡張するアノニマスクラスを作成しました:
val newThread = new Thread {
//creating anonymous class extending Thread
}
そして、このアノニマスクラスの本体で、私たちのタスクを定義しました(もう一度、 Runnable
インターフェースを実装するアノニマスクラスを作成します)。だから我々は、我々が使用することを考えているかもしれないpublic Thread(Runnable target)
コンストラクタを実際に(別売無視して()
我々が使用public Thread()
の本体で定義されて何もコンストラクタをrun()
メソッド。問題を解決するには、中括弧の代わりにかっこを使用する必要があります。
val newThread = new Thread ( new Runnable {
override def run(): Unit = {
// perform task
println("Performing task.")
}
}
)
言い換えれば、ここで{}
と()
は交換できません。
シングルトン&コンパニオンオブジェクト
シングルトンオブジェクト
Scalaは静的メンバーをサポートしますが、Javaと同じ方法ではありません。 Scalaはこれに代わるSingleton Objectsを提供します。シングルトンオブジェクトは、 new
キーワードを使用してインスタンス化することはできない点を除いて、通常のクラスに似ています。以下は、シングルトンクラスのサンプルです。
object Factorial {
private val cache = Map[Int, Int]()
def getCache = cache
}
「class」や「trait」ではなくsingletonオブジェクトを定義するためにobject
キーワードを使用していobject
。シングルトンオブジェクトはインスタンス化できないため、パラメータを持つことはできません。シングルトンオブジェクトへのアクセスは次のようになります。
Factorial.getCache() //returns the cache
これは、Javaクラスの静的メソッドにアクセスするのとまったく同じように見えることに注意してください。
コンパニオンオブジェクト
Scalaでは、シングルトンオブジェクトは対応するクラスの名前を共有できます。このようなシナリオでは、シングルトンオブジェクトはコンパニオンオブジェクトと呼ばれます。たとえば、クラスFactorial
が定義され、その下にコンパニオンオブジェクト( Factorial
という名前)が定義されています。慣例により、コンパニオンオブジェクトはコンパニオンクラスと同じファイルに定義されます。
class Factorial(num : Int) {
def fact(num : Int) : Int = if (num <= 1) 1 else (num * fact(num - 1))
def calculate() : Int = {
if (!Factorial.cache.contains(num)) { // num does not exists in cache
val output = fact(num) // calculate factorial
Factorial.cache += (num -> output) // add new value in cache
}
Factorial.cache(num)
}
}
object Factorial {
private val cache = scala.collection.mutable.Map[Int, Int]()
}
val factfive = new Factorial(5)
factfive.calculate // Calculates the factorial of 5 and stores it
factfive.calculate // uses cache this time
val factfiveagain = new Factorial(5)
factfiveagain.calculate // Also uses cache
この例では、プライベートcache
を使用して、反復数の計算時間を節約するために階乗を格納しています。
ここでは、 object Factorial
はコンパニオンオブジェクトであり、 class Factorial
は対応するコンパニオンクラスです。コンパニオンオブジェクトとクラスは、お互いのprivate
メンバーにアクセスできます。上記の例では、 Factorial
クラスは、そのコンパニオンオブジェクトのプライベートcache
メンバーにアクセスしています。
クラスの新しいインスタンス化では、同じコンパニオンオブジェクトが引き続き使用されるため、そのオブジェクトのメンバー変数への変更は引き継がれます。
オブジェクト
クラスは青写真に似ていますが、オブジェクトは静的です(つまり、すでにインスタンス化されています)。
object Dog {
def bark: String = "Raf"
}
Dog.bark() // yields "Raf"
彼らはしばしばクラスのコンパニオンとして使用され、あなたは次のように書くことができます:
class Dog(val name: String) {
}
object Dog {
def apply(name: String): Dog = new Dog(name)
}
val dog = Dog("Barky") // Object
val dog = new Dog("Barky") // Class
インスタンスの型チェック
タイプチェック : variable.isInstanceOf[Type]
パターンマッチング (この形式ではあまり役に立ちません):
variable match {
case _: Type => true
case _ => false
}
isInstanceOf
とパターンマッチングでは、配列を除いて、オブジェクトの型だけをチェックし、型パラメータを持たないジェネリックパラメータはチェックしません。
val list: List[Any] = List(1, 2, 3) //> list : List[Any] = List(1, 2, 3)
val upcasting = list.isInstanceOf[Seq[Int]] //> upcasting : Boolean = true
val shouldBeFalse = list.isInstanceOf[List[String]]
//> shouldBeFalse : Boolean = true
しかし
val chSeqArray: Array[CharSequence] = Array("a") //> chSeqArray : Array[CharSequence] = Array(a)
val correctlyReified = chSeqArray.isInstanceOf[Array[String]]
//> correctlyReified : Boolean = false
val stringIsACharSequence: CharSequence = "" //> stringIsACharSequence : CharSequence = ""
val sArray = Array("a") //> sArray : Array[String] = Array(a)
val correctlyReified = sArray.isInstanceOf[Array[String]]
//> correctlyReified : Boolean = true
//val arraysAreInvariantInScala: Array[CharSequence] = sArray
//Error: type mismatch; found : Array[String] required: Array[CharSequence]
//Note: String <: CharSequence, but class Array is invariant in type T.
//You may wish to investigate a wildcard type such as `_ <: CharSequence`. (SLS 3.2.10)
//Workaround:
val arraysAreInvariantInScala: Array[_ <: CharSequence] = sArray
//> arraysAreInvariantInScala : Array[_ <: CharSequence] = Array(a)
val arraysAreCovariantOnJVM = sArray.isInstanceOf[Array[CharSequence]]
//> arraysAreCovariantOnJVM : Boolean = true
型キャスト : variable.asInstanceOf[Type]
variable match {
case _: Type => true
}
例:
val x = 3 //> x : Int = 3
x match {
case _: Int => true//better: do something
case _ => false
} //> res0: Boolean = true
x match {
case _: java.lang.Integer => true//better: do something
case _ => false
} //> res1: Boolean = true
x.isInstanceOf[Int] //> res2: Boolean = true
//x.isInstanceOf[java.lang.Integer]//fruitless type test: a value of type Int cannot also be a Integer
trait Valuable { def value: Int}
case class V(val value: Int) extends Valuable
val y: Valuable = V(3) //> y : Valuable = V(3)
y.isInstanceOf[V] //> res3: Boolean = true
y.asInstanceOf[V] //> res4: V = V(3)
備考:これはJVMの動作に関するもので、他のプラットフォーム(JS、ネイティブ)の型キャスト/チェックでは動作が異なる場合があります。
コンストラクタ
プライマリコンストラクタ
Scalaでは、プライマリコンストラクタはクラスの本体です。クラス名の後には、コンストラクタ引数であるパラメータリストが続きます。 (他の関数と同様に、空のパラメータリストは省略することができます)。
class Foo(x: Int, y: String) {
val xy: String = y * x
/* now xy is a public member of the class */
}
class Bar {
...
}
val
キーワードでインスタンスメンバとしてマークされていない限り、インスタンスの構築パラメータはコンストラクタ本体の外部ではアクセスできません。
class Baz(val z: String)
// Baz has no other members or methods, so the body may be omitted
val foo = new Foo(4, "ab")
val baz = new Baz("I am a baz")
foo.x // will not compile: x is not a member of Foo
foo.xy // returns "abababab": xy is a member of Foo
baz.z // returns "I am a baz": z is a member of Baz
val bar0 = new Bar
val bar1 = new Bar() // Constructor parentheses are optional here
オブジェクトのインスタンスがインスタンス化されるときに実行される操作は、クラスの本文に直接記述されます。
class DatabaseConnection
(host: String, port: Int, username: String, password: String) {
/* first connect to the DB, or throw an exception */
private val driver = new AwesomeDB.Driver()
driver.connect(host, port, username, password)
def isConnected: Boolean = driver.isConnected
...
}
できるだけ副作用を少なくすることはコンストラクタに置くことをお勧めします。上記のコードの代わりに、消費者コードがIOのスケジューリングを担当するように、 connect
とdisconnect
方法を検討する必要connect
ありdisconnect
。
補助コンストラクタ
クラスには、補助コンストラクタと呼ばれる追加のコンストラクタがあるかもしれません。これらはdef this(...) = e
という形式のコンストラクタ定義で定義されdef this(...) = e
ここでe
は別のコンストラクタを呼び出さなければなりません:
class Person(val fullName: String) {
def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}
// usage:
new Person("Grace Hopper").fullName // returns Grace Hopper
new Person("Grace", "Hopper").fullName // returns Grace Hopper
これは、各コンストラクタが異なる修飾子を持つことができることを意味します。
class Person private(val fullName: String) {
def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}
new Person("Ada Lovelace") // won't compile
new Person("Ada", "Lovelace") // compiles
このようにして、コンシューマコードがクラスをインスタンス化する方法を制御することができます。