#はじめに
猿でも分かるScala!シリーズとして本記事ではScalaにおける型について基本的な事項を紹介していきます。紹介する項目は主に次のものです。
- クラスとしての型
- SymbolとStringの相違
- BigDecimal
- 数値と文字列の相互変更
Scalaでの型の扱いにはJavaと異なる部分があります。型がクラスとして扱われたり、BigDecimalの演算がJavaよりも直感的になったりする特徴があります。以下ではこのようなScalaの型に関する特徴を紹介します。
では早速始めましょう!
##クラスとしての型
ScalaではJavaのプリミティブ型(int, double等)に相当する型は存在せず、全てクラスとなっています。Javaのプリミティブ型に相当する型は、Scalaの全ての親クラスであるAnyクラスを継承したAnyValクラスのサブクラスになっています。java.lang.Objectを親に持つ全ての Java オブジェクトは Anyクラスの子クラスであるAnyRef型として扱うことができます(ScalaではSymbol, String, BigInt, BigDecimalはAnyRefに所属します)。
Scalaの型にはどのようなものがあるのか確認してみましょう。下の表を見るとScalaの型はJavaとほとんど変わらないことがわかると思います(SymbolはScala特有です)。
型名 | 説明 | 例 | デフォルト値 | 親クラス |
---|---|---|---|---|
Boolean | trueかfalse | true, false | false | AnyVal |
String | 文字列, 同じ内容でも異なるインスタンスが存在 | "text" | null | AnyRef |
Char | 16ビット符号なしUnicode文字 | 'text' | '\0' | AnyVal |
Symbol | 文字列, 同じ内容ならインスタンスも等しい | 'text, Symbol("text") | null | AnyRef |
Byte | 8ビット符号付き整数値 | -128, 127 | 0 | AnyVal |
Short | 16ビット符号付き整数値 | -32768, 32767 | 0 | AnyVal |
Int | 32ビット符号付き整数値 | -2147483648, 2147483647 | 0 | AnyVal |
Long | 64ビット符号付き整数値 | -2^63, 2^63-1 | 0 | AnyVal |
Float | 32ビットIEEE 745倍精度浮動小数点数 | 1.2F, 1.2e2f | 0.0 | AnyVal |
Double | 64ビットIEEE 745倍精度浮動小数点数 | 1.2, 1D, 1.2e2 | 0.0 | AnyVal |
BigInt | 任意精度の整数 | BigInt(10) | null | AnyRef |
BigDecimal | 任意精度の固定小数 | BigDecimal(10.54) | null | AnyRef |
ここでnビットは2^n種類の値を保存できることを示しています。例えば8ビット符号付き整数値のときは256個の整数を-128から127まで変数に保持できることをしましています。
JavaとScalaでは変数の宣言や初期化の方法が異なっています。Javaの場合は変数を例えばInt x = 0
のように初期化します。Scalaの場合に、何らかの数値を格納する変数xを宣言するとき
val または var x:型名
で行います。初期化をするときは例えば
val x:Int = 100
のようにします。
変数の初期化でJavaと大きく異なる点は、Scalaが便利な __型推論__の機能を持ってることです。これによって型名を明記せず初期化をすることが可能です。例えばval x = 100
と書くとxは __Int型__とされます。またval x = 100.0
と書くと、少数点から空気を読んでxは __Double型__とされます。
上記の表の「デフォルト値」というのは、 __varで変数を宣言__したときに「_」(アンダースコア)を指定した場合の初期値です。例えば
var x:Int = _
の場合、変数が0で初期化されます。
##SymbolとStringの相違
文字列は同じ内容でもインスタンスとしては異なる場合があるのに対し、シンボルは 同じシンボルリテラルを複数書いても全く同一のSymbolオブジェクトを参照します。例えば文字列として、
val str1 = new String("A")
val str2 = new String("A")
と同じ"A"を持つstr1, str2を定義します。インスタンスとして同一か判定するためにeq
メソッドでstr1 eq str2
と書くと、Boolean = false
と返ってきます。一方、Symbolで
val symb1 = Symbol("A")
val symb2 = Symbol("A")
と定義し、symb1 eq symb2
で比較してもBoolean = true
となります。
##BigDecimal
BigDecimalは任意精度の固定小数点数です。10進法演算を行います。わかりやすく言うと、小数点以下も可能な限り丸めたりせず正確に表示します。
BigDecimalの変数の生成は、例えば次のようになります。
val num = BigDecimal(200)
val num2 = BigDecimal(1.005)
四則演算もInt型やDouble型の変数と同様に実行することができます。
num + num2
num - num2
num * num2
num / num2
一方、Javaの場合はBigDecimalで処理されたある変数num1, num2の四則演算はScalaのように直感的ではありません。例えば和を計算するのにnum1.add(num1)
のように書きます。ScalaのBigDecimalは通常の四則演算と同じ感じで計算できるので便利です。
有効数字の桁数指定や四捨五入、切り捨て、切り上げは次のようにsetScale()
メソッドにより可能です。例えばval num3 = num / num2
に対して小数点以下4桁を表示、5桁目を四捨五入、切り上げ、切り捨てするには上から順に次のような文になります。
val num3 = num3.setScale(4, scala.math.BigDecimal.RoundingMode.HALF_UP)
val num4 = num3.setScale(4, scala.math.BigDecimal.RoundingMode.CEILING)
val num5 = num3.setScale(4, scala.math.BigDecimal.RoundingMode.FLOOR)
##数値と文字列の相互変更
データの型をString型に変換するためにはtoString()
メソッドを使って次のように簡単に書けます。
val str = x.toString
変数xの型はString以外のどの型でも可能です。
今度は文字列から各データ型への変換をみてみます。String型のある変数がval xStr
で定義されていたとします。このとき変数の型に合わせて変換は次のようになります。
val xBoolean = xStr.toBoolean
val xChar = xStr.charAt(n)
val xSymb = Symbol(xStr)
val xByte = xStr.toByte
val xShort = xStr.toShort
val xInt = xStr.toInt
val xLong = xStr.toLong
val xFloat = xStr.toFloat
val xDouble = xStr.toDouble
ここでcharAt(n)
はxStrのCharシークエンスを左から数えてn-1番目をChar型にするという意味です。
##まとめ
ここまでScalaの型について紹介してきました。Scalaの基本的な型の種類はJavaと同一ですが、型がクラスとして扱われる点がJavaとことなる点でした。また型はAnyクラスの子クラスであるAnyRefまたはAnyValに所属することも見てきました。BigDecimalはJavaよりも直感的に操作できることを紹介しました。Scalaでは文字列への変換が簡単にできる等の特徴も紹介してきました。
Scalaは非常に簡単に処理を実行できる言語なので、本章で紹介したような手短な操作をどんどん使ってコードを簡潔で読みやすいものにしたいですね!