S-JIS[2010-10-29/2017-01-18] 変更履歴
Scalaの演算子のメモ。
|
|
|
Javaの演算子に対してScalaでどのような演算子(メソッド)を使うかの比較。
Java(Javaの演算子の優先度順) | Scala | 説明 | |||
---|---|---|---|---|---|
演算子 | 概要 | 例 | 演算子 メソッド |
例 | |
( ) | 優先する式 | (1 + 2) |
( ) { } |
(1 + 2) |
|
new. [] |
新規生成 | new 型[n] |
new | new Array[型](n) |
→配列 |
new クラス() |
new | new クラス() |
→クラス | ||
メンバー | obj.value |
. |
obj.value |
||
obj.method() |
. |
obj.method() |
|||
配列 | array[0] |
() | array(0) |
→配列 | |
++ -- | 後置inc/dec | n++ |
インクリメント・デクリメントはScalaには無い。 →xuweiさんのscalaでは、なぜインクリメントやデクリメントができないのか? |
||
++ -- + - ~ ! ( ) |
前置inc/dec | ++n |
|||
符号 | +n |
+ - | +n |
正符号・負符号 | |
ビット否定 | ~n |
~ | ~n |
ビット反転 | |
論理否定 | !b |
! |
!b |
trueをfalseに、falseをtrueにする | |
キャスト | (型)obj |
asInstanceOf | obj.asInstanceOf[型] |
→asInstanceOf | |
* / % | 乗算・除算・剰余 | m * n |
* / % | m * n |
|
+ - | 加算・減算 | m + n |
+ - | m + n |
|
<< >> >>> | ビットシフト | n << r |
<< >> >>> | n << r |
|
< > <= >= instanceof |
大小比較 | m < n |
< > <= >= |
m < n |
|
型比較 | obj instanceof クラス |
isInstanceOf | obj.isInstanceOf[クラス] |
→isInstanceOf | |
== != | 等価 不等価 |
m == n |
== != eq ne |
m == n |
JavaとScalaでは比較内容と優先順位が異なる。 |
& | ビットAND | m & n |
& | m & n |
|
^ | ビットXOR | m ^ n |
^ | m ^ n |
|
| | ビットOR | m | n |
| | m | n |
|
&& | 条件AND | b1 && b2 |
&& | b1 && b2 |
|
|| | 条件OR | b1 || b2 |
|| | b1 || b2 |
|
? : | 条件 | b ? m : n |
if | if (b) m else n |
Scalaでは三項演算子(条件演算子)は無いが、if式で代用。 |
= *= /= %= += -= <<= >>= >>>= &= ^= |= |
代入 | m = n |
= *= /= %= += -= <<= >>= >>>= &= ^= |= |
m = n |
Scalaでは代入式の戻り値はUnit。 |
Scalaでは、数値(IntやLong、Double等)もクラスであり、それらに対する演算(演算子)はそのクラスのメソッドとして定義されている。
例えば「+」は「+」という名前のメソッドであり、「1+2
」は「1.+(2)
」と書くことも出来る。
(逆に、もしIntにplusというメソッドが定義されていたら、「1.plus(2)
」や「1 plus 2
」と書くことが出来る。ピリオドを使わない書き方を中置記法と言う)
(メソッド呼び出しも含めた)各種の式の優先度は決まっている。
また、同優先度のメソッド同士では、メソッド名によって優先順位が変わる。
(四則演算には乗除算(*/)が加減算(+-)よりも優先度が高いといったルールがあるが、Scalaでは演算子はメソッドであり、メソッド名によって優先度が決まっている。
基本的にはメソッド名の先頭1文字によるが、末尾1文字が影響したりする。
→識別子(メソッド名・演算子)に使える記号)
Scalaの式(優先度順) | 例 | 結合 | 備考 | ||
---|---|---|---|---|---|
型推定 | |||||
リテラル | 123 |
→リテラル | |||
null | null | null |
→Null型 | ||
識別子 | 変数名など | ||||
thisとsuper | this super |
this |
|||
関数呼び出し | |||||
メソッドを返す無名関数 | |||||
型引数 | [ ] | →ジェネリクス(パラメータ化された型) | |||
タプル | ( , ) | →Tuple | |||
new | new | ||||
ブロック式 | { } | →ブロック式(複文) | |||
前置メソッド | + - ! ~ | +n |
右 | 正符号・負符号・否定・ビット反転 →「unary_」で定義する |
|
引数のあるメソッド | method() |
メソッド名の 先頭1文字 |
|||
下記以外の記号 | 左 | 例えば「obj1 # obj2」とか作れる(?) | |||
* / % |
m * n |
左 | 乗算・剰余算 | ||
+ - |
m + n |
左 | 加減算 | ||
: |
a :: list |
右 | メソッド名の末尾文字がコロン「: 」の場合は、右結合。(その他は左結合) →コロンで終わるメソッド名(演算子)の場合、演算子の左右を入れ替えて記述する |
||
= ! |
m == n |
左 | Javaではシフトや比較演算の方が等号・不等号より優先度が高いが、 Scalaでは逆。 しかし「1 << 2 == 4」は「1<<2」の方が先に計算される。不思議。 |
||
< > |
m > n |
左 | Javaではシフト演算の方が比較演算より優先度が高いが、 Scalaでは同順位。 左結合なので、左側にある方が先に計算される。 |
||
& |
m & n |
左 | |||
^ |
m ^ n |
左 | |||
| |
m | n |
左 | |||
記号以外の文字 |
obj.method(arg) |
左 | →メソッド呼び出し | ||
引数の無いメソッド | method | obj.method |
左 | →引数リストの無いメソッド呼び出し | |
代入演算子の演算部分 | 比較演算「>=」「<=」「==」「!=」以外で末尾が「=」だと代入演算子とみなされる。 →末尾「_=」も代入。呼び出し時に「=」を付けるのも代入。 |
||||
帰属型 | |||||
帰属注釈 | |||||
代入 | = | 変数 = 値 |
→代入結果はUnit。つまり値を返さない。 | ||
if式 | if else | if (条件) 値 else 値 |
|||
while式 | while | while (条件) 文 |
|||
do式 | do while | do 文 while(条件) |
|||
for式 | for | for (変数 <- 範囲) 文 |
|||
return式 | return | return 値 |
|||
throw式 | throw | throw 例外 |
|||
try式 | try catch finally | try { 文 } |
参考: Scala開眼の3階:文と式
Javaでは「==」「!=」の比較は、プリミティブ型の場合は値の比較だが、参照型の場合は参照(インスタンス)そのものが同じインスタンスを指しているかどうかを判定する。
参照型で値が等しいかどうかを判定したい場合はequals()メソッドを使う。
Scalaでは、「==」「!=」で値が等しいかどうかを比べる。(数値も文字列もその他のクラスも同様。実体としてはequals()メソッドが呼ばれる)
インスタンスが同一かどうかを調べる為には「eq」「ne」を使う。(Javaの「==」「!=」に相当)
(なお、==
, !=
はAnyのメソッド、eq
,
ne
はAnyRefのメソッドという位置づけ。[2017-01-18])
したがって、nullとの比較は「obj eq null」で行う。
が、「x == that
」は具体的には「if(x eq
null){ that eq null }else{ x.equals(that) }
」と同じなので、「obj == null」で比較しても良い。[2017-01-18]
(参考:
gakuzzzzさんのツイート)
ただし、(なぜか)配列だけは異なる挙動を示す。
数値の例 | 文字列の例 | 配列の例 | ||||
---|---|---|---|---|---|---|
== | 1 == 1 |
true | "abc" == "abc" |
true |
Array(1,2,3) == Array(1,2,3) |
false |
eq | 1 eq 1 |
エラー | "abc" eq "abc" |
true |
Array(1,2,3) eq Array(1,2,3) |
false |
Scalaの式の優先度の表によると、Scalaでは等値比較「==」の方が大小比較「<」やシフト演算「<<」より優先度が高い(Javaと逆)。
しかし以下の式を試してみると、正常に計算される。(「<<」の方が優先度が高い)
scala> 1 << 2 == 4 res185: Boolean = true scala> (1 << 2) == 4 res186: Boolean = true scala> 1 << (2 == 4) <console>:6: error: overloaded method value << with alternatives: (Long)Int <and> (Int)Int cannot be applied to (Boolean) 1 << (2 == 4) ^
シフト演算の結果はBooleanにはならない為、(優先度トップの)型推論でも働いて上手く調整してくれてるんだろうか?
あるいは、自分で演算子を作って同じ型を返すようにしたら、表どおりの優先度になるのかな?
Javaでは、キャストできるかどうか調べてOKの場合にキャストするのは、以下の様に書く。
(instanceofがtrueなら、必ずそのクラスにキャストできる)
if (obj instanceof クラス) { final クラス v = (クラス)obj; 〜 }
Scalaでは、それぞれ判定用・変換用のメソッドがあるので、以下の様に書ける。
if (obj.isInstanceOf[クラス]) { val v = obj.asInstanceOf[クラス] 〜 }
ただし、Scalaではmatch式を使う方がスマート。[2011-04-10]
obj match { case v: クラス => 〜 case _ => }
ちなみに、isInstanceOf・asInstanceOfが長めの名前になっているのは、「あまり使うな」という意思表示らしい。
※isInstanceOf・asInstanceOfは、Anyクラスのメソッドという位置づけになっている
isInstanceOfとasInstanceOfを試しているときに、判定結果と変換可否が必ず対応するとは限らないようだ。と思ったが。
(isInstanceOf[Byte]はfalseを返すのに、asInstanceOf[Byte]で変換できる)
scala> var obj = 1 obj: Int = 1 scala> obj.isInstanceOf[Byte] res183: Boolean = false scala> obj.asInstanceOf[Byte] res184: Byte = 1
考えてみると、Javaでもintはbyteではないがbyteへキャストできるから、それと同じか。[2011-04-10]
また、isInstanceOf[AnyVal]は使うことは出来ない。[2010-12-12]
scala> val n = 1
n: Int = 1
scala> n.isInstanceOf[AnyVal]
<console>:10: error: type AnyVal cannot be used in a type pattern or isInstanceOf test
n.isInstanceOf[AnyVal]
^
isInstanceOf[AnyRef]は(AnyRefでなくAnyValに属しているはずのクラスでも)常にtrueになる。
(数値はオートボクシングによりJavaのラッパークラスになり、それに「instanceof Object」するので(?)、当然常にtrueになる)
scala> n.isInstanceOf[AnyRef] res13: Boolean = true scala> ().isInstanceOf[AnyRef] res14: Boolean = true scala> false.isInstanceOf[AnyRef] res15: Boolean = true
キャストは、asInstanceOfの他に「:
型」という方法もある。[2011-06-21]
ただし、まるっきり同じというわけではない。
asInstanceOf | : 型 |
備考 |
---|---|---|
scala> 127.asInstanceOf[Byte] res0: Byte = 127 scala> 128.asInstanceOf[Byte] res1: Byte = -128 |
scala> 127:Byte
res3: Byte = 127
scala> 128:Byte
<console>:8: error: type mismatch;
found : Int(128)
required: Byte
128:Byte
^
|
「: 型」だと、範囲外の数値はエラーになる。 |
scala> 1.asInstanceOf[String]
java.lang.ClassCastException: java.lang.Integer
cannot be cast to java.lang.String
|
scala> 1:String
<console>:8: error: type mismatch;
found : Int(1)
required: String
1:String
^
|
|
Javaで例えると、キャストして代入?String s = (String)1; |
Javaで例えると、キャストせずに代入?String = 1; |
キャストしない場合、コンパイル時に型(クラス)が不一致でエラーになる。 キャストする場合、コンパイルは通る(ように変換される)が実行時に失敗する。 ということだろうか? |
Javaでは、代入式は代入された値を返す。したがって下記のような書き方で変数を初期化できる。
(「c = 1」は1を返すので、「b = c」はbに1を入れて1を返す。したがってaに1が入る)
int a, b, c; a = b = c = 1;
しかしScalaでは代入式は戻り値を返さない(厳密にはUnit型なので、「()
」を返す)ので、こういった書き方は出来ない。
ということは、Unit型の変数なら出来るかも…?
scala> var a: Unit = _ a: Unit = () scala> var b: Unit = _ b: Unit = () scala> var c: Unit = _ c: Unit = () scala> a = b = c = () a: Unit = ()おー、出来た。けどUnit型の変数は「
()
」しか値を持てないから、意味無ぇ(爆)
これが出来ないと何が困るかというと、Javaでの以下のようなコーディングが出来なくなる。
BufferedReader br = new BufferedReader(〜); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); }
個人的にはこの書き方は好きじゃない。代入用のローカル変数をスコープの外で定義しないといけないから。
(C言語のFILEのオープン方法の頃からあまり好きじゃなかったなぁ)
なので元々使ってないから、自分は特に困らないけどね。
ただ、似たような書き方をする事は出来る。[2011-07-11]
→テキストファイルの読み込み(while ({ line = br.readLine();
line ne null }))
Scalaの代入がUnitを返すのは、「副作用がある処理はUnitを返す」という論理からすると、素直な考え方らしい。[2012-03-07]
ちなみに、変数の定義時点であれば、複数の変数をまとめて初期化できる。[2011-01-15]
scala> val a, b, c = 1 a: Int = 1 b: Int = 1 c: Int = 1