S-JIS[2010-12-12/2017-01-18] 変更履歴

Scala Any

ScalaのAny・AnyVal・AnyRefクラス関連のメモ。


概要

Javaの全てのクラスがObjectクラスから派生しているように、ScalaではAnyクラスから派生している。

Anyの直下(直接の子クラス)にはAnyRefAnyValがある。
AnyValはIntやLong・Double等の値のクラスを表す。(Javaのプリミティブ型に相当)
AnyRefはそれ以外のクラスを表す。(Javaの参照型に相当)

nullAnyRefの全てのクラス(の変数)に代入できる。
nullはNullというクラスの値になっており、Null型は全てのAnyRefの子クラスの下に位置づけられる。
(したがって、ScalaではInt等も含めて全ての型がオブジェクトということになっているけれども、Int等(のAnyValの派生クラス)の変数にはnullを代入できない)

AnyVal・AnyRefも含めて全てのクラスの子クラスとしてNothingが存在する。

継承関係
Any 最上位
AnyVal AnyRef





Unit
Int
Long
Double
通常のクラス
(Java・Scala)
Null
Nothing 最下位

Anyクラス

scala.Anyクラスは、Scalaの全ての親クラス。

メンバー 説明 備考
def ==(arg: Any) : Boolean オブジェクトの値(内容)が等しいかどうかを返す。
自分自身がnullの場合はargもnullのときtrue。[2017-01-18]
null以外の場合はequals()メソッドを呼び出す。
したがって、nullと比較するときは「obj == null」でも良い。
==とeqの違い
def !=(arg: Any) : Boolean オブジェクトの値(内容)が等しくないかどうかを返す。
実体としては「!(自分==arg)」。
 
def ##() : Int ハッシュコードを返す。[2017-01-18]
null.##」は0、それ以外はhashCode()と同じ。
 
def hashCode() : Int JavaのObject#hashCode()と同様。[2017-01-18]
null.hashCode()はNullPointerExceptionになる。
 
def isInstanceOf[T] : Boolean クラスTの子クラスかどうか(Tにキャストできるかどうか)を返す。 使用例
def asInstanceOf[T] : T クラスTにキャストする。

ただしクラスとしての実体(ソースファイル)は無いっぽい。


AnyRefクラス

scala.AnyRefクラスは、全ての参照型の親クラス。
実体としてはJavaのjava.lang.Objectに該当し、Objectクラスのメソッドを呼び出せる。

メンバー 説明
def ==(arg: AnyRef) : Boolean オブジェクトの値(内容)が等しいかどうかを返す。
自分自身がnullの場合はargもnullのときtrue。
null以外の場合はequals()メソッドを呼び出す。
したがって、nullと比較するときは「obj == null」でも良い。[2017-01-18]
def !=(arg: AnyRef) : Boolean オブジェクトの値(内容)が等しくないかどうかを返す。
def eq(arg: AnyRef) : Boolean インスタンスが同一かどうかを返す。
Javaの「==」に相当。
def ne(arg: AnyRef) : Boolean インスタンスが同一でないかどうかを返す。
実体としては「!(自分 eq arg)」。
def getClass() : java.lang.Class オブジェクトのクラスを返す。
JavaのObject#getClass()と同様。
def ##() : Int ハッシュコードを返す。
null.##」は0、それ以外はhashCode()と同じ。[/2017-01-18]
def hashCode() : Int JavaのObject#hashCode()と同様。
null.hashCode()はNullPointerExceptionになる。[2017-01-18]
def synchronized[T](arg: T) : T 排他制御を行う。[2011-02-26]
Javaのsynchronizedに相当。

AnyValクラス

ScalaではJavaのプリミティブ型に当たる型は無く、全てクラスとなっている。[2010-10-23]
AnyValは、そのInt・Long・Double等のクラスの親クラス。UnitクラスもAnyValに含まれる。

(パッケージ)クラス Java相当 説明 値(の範囲) 初期値 例(リテラル
scala Unit void 型無し () ()  
scala Boolean boolean 真偽値 true, false false true
scala Char char Unicode文字 シングルクォーテーションで囲む '\0' 'a'
scala Byte byte 1バイト整数 scala.Math.MIN_BYTE〜MAX_BYTE 0 10.toByte
scala Short short 16bit整数 scala.Math.MIN_SHORT〜MAX_SHORT 0 10.toShort
scala Int int 32bit整数 scala.Math.MIN_INT〜MAX_INT 0 123
scala Long long 64bit整数 scala.Math.MIN_LONG〜MAX_LONG 0 123L
scala Float float 32bit浮動小数 scala.Math.MIN_FLOAT〜MAX_FLOAT 0.0 123.0F
scala Double double 64bit浮動小数 scala.Math.MIN_DOUBLE〜MAX_DOUBLE 0.0 123.0D
以下のクラスはAnyValでなくAnyRefだが、ついでに。
scala Symbol   シンボル 文字列の前にシングルクォーテーションを付ける null 'zzz
scala.math BigInt BigInteger 任意精度の整数   null BigInt(10)
scala.math BigDecimal BigDecimal 任意精度の固定小数   null BigDecimal(10)

Javaと違い、BigInt・BigDecimalにも四則演算(+-*/)が定義されている。
Scalaの演算子


値クラス(Value Classes)

Scala2.10でAnyValを継承した値クラスが定義できるようになった。[2013-06-08]

値クラスにすると、そのインスタンスを使う箇所において“内部で保持している値を直接使う”ようなコードに変換されるので、実行効率が良い。
(値として直接使えない箇所ではインスタンスとして扱われる)

参考: Eugene Yokotaさんの値クラスと汎用トレイト


//例
class MyString(val s: String) extends AnyVal {
  def isUpper() = s.forall(_.isUpper)
}

値クラスを定義する際は、AnyValをextendsする。
そして基本コンストラクター(クラス名の直後の引数部分)にpublicでvalな引数を必ず1つ(のみ)指定する。
(複数の引数を指定するとコンパイルエラーになる)

クラスのボディー部にはdefしか定義できない。(valやvar、内部classやtrait・objectを定義できない)

汎用トレイト(Anyから派生し、defしか無く、初期化を一切行わないトレイト)をミックスインすることは出来る。

implicit classの例


Unit

Unitクラスは、JavaやC言語のvoidと同様に使う。つまりメソッド・関数において戻り値(返り値)が無い場合に指定する。[2010-11-28]

Scala Java
def メソッド() : Unit = {
	〜
}
def メソッド() {
	〜
}
void メソッド() {
	〜
}

ただしUnitは、実際には「値が無い」のではなく、唯一の値「()」を持つ。
例えば「Byteは-128〜127の256種類の値を持つ」「Booleanはtrueとfalseの2種類の値を持つ」というのと同様に、Unitは()という1種類の値だけを持つ。
したがってUnitを返すメソッドというのは、常に「()」しか返らないので、「戻り値に意味が無い」という事になる。
Unitは常に同じ値しか返さないので値に意味が無い(違いが無い)、したがって“使い方はvoidと同様”という事なのだろう。

ちなみに、「()」は要素数が0個のタプル(空のタプル)らしい。

なお、実際に「値が無い」ということを表すNothingというクラスがある。


Scalaでは、メソッドの戻り値や変数の代入先の型に応じて値を変換してくれる。[2011-06-12]
def メソッド(): Int = "a"」は、"a"(String)をIntに変換しようとするが出来なくてコンパイルエラーになる。
def メソッド(): Unit = "a"」だとコンパイルは通るので、"a"(String)からUnitへ変換しているように見える。
しかしコンパイルされると戻り型はJavaのvoidになるので、戻り値は単純に捨てられる。Unitへ変換されるわけではない。

これを逆コンパイルして見てみた。

Scala コンパイルされたJavaのイメージ 備考
class A {
  def メソッド() : Unit = {
    "a"
  }
}
public class A implements ScalaObject {
    public void メソッド() {
        String tmp = "a";
    }
}
テンポラリーの変数に代入して終わり。
戻り型がvoidなので、値を返していない。
class A {
  val s: Unit = "a"
}
public class A implements ScalaObject {
    private final BoxedUnit s;

    public A() {
        String tmp = "a";
        s = BoxedUnit.UNIT;
    }

    public void s() { }
}
Unitを表すクラス(BoxedUnit)はあるようだが、BoxedUnit.UNITという固定値が入れられている。
名前からすると、(String→Unitの暗黙変換ではなくて、)オートボクシングの扱いなのかな?

Scalaではpublicなフィールドにはアクセスする為のメソッドが用意されるが、
sの値を返すメソッドの戻り型もvoidになっている。
このメソッドを呼び出す側も、s()の戻り値を使わず直接BoxedUnit.UNITを使っている。
val a = new A
val t = a.s
final A a = new A();
final BoxedUnit t = BoxedUnit.UNIT;

Nothing

UnitはJavaやC言語のvoidと同様の使い方をするが、実際には値が無いわけでは無い。[2010-11-28]

「値が無い」という意味では、Nothingというクラスがある。
こちらは本当に値が無くて、Nothingを返すよう宣言したメソッドでは、値を返す事が出来ない。

Nothing Unit
scala> def f():Nothing = { println(1) }
<console>:5: error: type mismatch;
 found   : Unit
 required: Nothing
       def f():Nothing = { println(1) }
                                  ^
scala> def f():Unit = { println(1) }
f: ()Unit

scala> val v = f()
1
v: Unit = ()

例えばプログラムの実行を終了するexit()関数は、戻り型がNothingで定義されている。
(プログラムが終了してしまうので、呼び出し元に制御が戻ることが無い。つまり返す値が無い(値が返ることが無い))

scala> def f():Nothing = { exit(-1) }
f: ()Nothing

あるいは、常に例外が発生するメソッドでは、戻り型をNothingにすることが出来る。
(使い道は…よく分からない^^;)

scala> def f():Nothing = { throw new Exception() }
f: ()Nothing

余談だが、メソッドの戻り型を省略した際の型推論もちゃんと上記のようになる。

scala> def f() = exit(0)
f: ()Nothing

scala> def f() = throw new Exception()
f: ()Nothing
scala> def f() = println(1)
f: ()Unit

scala> def f() = {}  //メソッド本体が空
f: ()Unit

scala> def f() = ()  //Unitの値を指定
f: ()Unit

Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま