scala の asInstanceOf isInstanceOf classOf について

  • asInstanceOf
  • isInstanceOf
  • classOf

の3つはscalaのキーワードというか予約語ではないです。文法上はあくまでメソッドです。けどよくよく考えると半分予約語というか、特殊な存在です。
なぜ特殊な存在かというと、これらはすべて型引数を1つだけとります。そして、実際の引数は取りませんjavaでもscalaでもそうですが、受け渡す型引数によって、実行時に戻り値を変更するというのはできないはずです*1

javaを知っている人にとっては、この3つに関して、そのまま対応するものがあるのでわかりやすいはずです。以下その対応表

説明 scala java
一般的にいうキャスト val str = obj.asInstanceOf[String] String str = (String)obj;
あるインスタンスが、その型かどうかを調べる if(obj.isInstanceOf[String]) print("objはStringです") if(obj instanceof String) print("objはStringです");
型から、その型のjava.lang.Classクラスのインスタンスを取得する val clazz = classOf[String] Class clazz = String.class;

使い方だけだったら、javaわかる人はこれだけでわかると思うんですが、もうちょっと突っ込んで内部の仕組みというか、文法上どういう風に扱われているのかを調べてみます。
まずasInstanceOfとisInstanceOfですが、ライブラリのどこかのソースコード上に定義されているものではありませんAnyに定義されているものです。そして、Any自体のソースコードはありません。*2
*3

そして、classOfについてはPredefに以下のように定義されてます。

  /** Return the runtime representation of a class type.  This is a stub method.
   *  The actual implementation is filled in by the compiler.
   */
  def classOf[T]: Class[T] = null

Predefに定義されているということは、感覚的はグローバル関数のように使用できるわけです。
classOfのコメントをみると、"実際の実装はコンパイラがやるよ"みたいなことがかいてありますね。

そして、実際どうコンパイルされるのかを調べるために、お馴染みの scalac -> jad をします(`・ω・´)

もとのscalaソースコード

object Test{

  val str = "abcde"

  val a = str.asInstanceOf[CharSequence]
  
  val b = str.isInstanceOf[Boolean]

  val c = classOf[List[String]]

}

コンパイルしたあと、jadで逆コンパイルしたもの↓

import scala.ScalaObject;
import scala.collection.immutable.List;

public final class Test$
    implements ScalaObject
{

    public String str()
    {
        return str;
    }

    public CharSequence a()
    {
        return a;
    }

    public boolean b()
    {
        return b;
    }

    public Class c()
    {
        return c;
    }

    private Test$()
    {
        b = str() instanceof Boolean;
    }

    public static final Test$ MODULE$ = this;
    private final String str = "abcde";
    private final CharSequence a = str();
    private final boolean b;
    private final Class c = scala/collection/immutable/List;

    static 
    {
        new Test$();
    }
}
public final class Test
{

    public static final Class c()
    {
        return Test$.MODULE$.c();
    }

    public static final boolean b()
    {
        return Test$.MODULE$.b();
    }

    public static final CharSequence a()
    {
        return Test$.MODULE$.a();
    }

    public static final String str()
    {
        return Test$.MODULE$.str();
    }
}

予想通りというか、大して面白く無いですね(´・ω・`)

ここからはあくまで個人的な推測です。
この3つをメソッドにした理由は、おそらく単純に、予約語を増やしたくなかったんでしょうかね?予約語でなく、メソッドとして扱っておけば、構文の解析がその分楽になるんじゃないかと。*4おそらく同じような理由で、volatilenativetransientなども、予約語ではなく*5アノテーションになってるのではないかと思います。*6
あと、synchronizedも予約語ではなくAnyRefのメソッドという扱いですね。

*1:まちがってたら誰か突っ込んでください。実行時というのがポイントです

*2:仮想的にソースコード自体はつくろうと思えば作れますけど、コンパイラがそのソースコードもとに、Anyを他のクラスと全く同じように扱って解析し、classファイルを生成するというのが不可能です。Anyはコンパイルした後には存在しないものなので、コンパイル時のどこかのフェーズでAnyを特別扱いをしないと

*3: 2011/5/8追記 最近(2.9.0)になって、おそらくScaladoc用のために仮想的なソースコードおいてるみたいですね。NothingやNullもある

*4:javaだと、この3つが、すべて構文に組み込まれています

*5:javaでは同名の予約語。働きは全く同じ

*6:もしこの辺の事情詳しい人いたら教えてください