2012年7月21日土曜日

[Scala] 変位指定と isInstanceOf

前回の 変位指定 の続き。
変位指定付きの型のオブジェクトを isInstanceOf[ T ] で検査した場合の挙動を確認しておこう。

まずは共変から。

object VarianceSample {
  def main(args : Array[String]) {
    var covariantStr : Covariantable[String] = new Covariantable[String];
    var covariantAny : Covariantable[AnyRef] = new Covariantable[AnyRef];

    println(
       "Covariantable[String] is instance of Covariantable[AnyRef]: " 
        + covariantStr.isInstanceOf[Covariantable[AnyRef]]);

    println(
        "Covariantable[AnyRef] is instance of Covariantable[String]: " 
        + covariantAny.isInstanceOf[Covariantable[String]]);
  }
}

class Covariantable[+R] {
  def hoge(): R = null.asInstanceOf[R];
}


Covariantable[String] is instance of Covariantable[AnyRef]: true Covariantable[AnyRef] is instance of Covariantable[String]: true

あれ?
共変なので、[String] is instance of [AnyRef] は true でも反対は false と思ったのだが、 両方とも true になった。


共変・反変・非変 の is instance of

念のため全部調べてみよう。

object VarianceSample {
  def main(args : Array[String]) {
    // 共変
    var covariantStr : Covariantable[String] = new Covariantable[String];
    var covariantAny : Covariantable[AnyRef] = new Covariantable[AnyRef];

    println(
        "Covariantable[String] is instance of Covariantable[AnyRef]: " 
        + covariantStr.isInstanceOf[Covariantable[AnyRef]]);

    println(
        "Covariantable[AnyRef] is instance of Covariantable[String]: " 
        + covariantAny.isInstanceOf[Covariantable[String]]);
      
    // 反変
    var contraStr : Contravariantable[String] 
        = new Contravariantable[String]();
    var contraAny : Contravariantable[AnyRef] 
        = new Contravariantable[AnyRef]();

    println(
      "Contravariantable[String] is instance of "
      + "Contravariantable[AnyRef]: " 
      + contraStr.isInstanceOf[Contravariantable[AnyRef]]);

    println(
      "Contravariantable[AnyRef] is instance of "
      + Contravariantable[String]: " 
      + contraAny.isInstanceOf[Contravariantable[String]]);      

    // 非変
    var nonvariantStr : Nonvariantable[String] 
        = new Nonvariantable[String];
    var nonvariantAny : Nonvariantable[AnyRef] 
        = new Nonvariantable[AnyRef];
      
    println(
      "Nonvariantable[String] is instance of Nonvariantable[AnyRef]: " 
      + nonvariantStr.isInstanceOf[Nonvariantable[AnyRef]]);

    println(
      "Nonvariantable[AnyRef] is instance of Nonvariantable[String]: " 
      + nonvariantAny.isInstanceOf[Nonvariantable[String]]);  
  }
}
class Contravariantable[-T] {
  def hoge(t : T) {};
}

class Covariantable[+R] {
  def hoge(): R = null.asInstanceOf[R];
}

class Nonvariantable[T] {
  def hoge(t : T) {};
}


Covariantable[String] is instance of Covariantable[AnyRef]: true Covariantable[AnyRef] is instance of Covariantable[String]: true Contravariantable[String] is instance of Contravariantable[AnyRef]: true Contravariantable[AnyRef] is instance of Contravariantable[String]: true Nonvariantable[String] is instance of Nonvariantable[AnyRef]: true Nonvariantable[AnyRef] is instance of Nonvariantable[String]: true

全部 true だった。
確かに Java の instanceof では ジェネリクスの型指定のチェックができないから仕方がないのかも。 Java では instanceof の右側は型パラメータを指定する場合には <?> にしないとエラーになる。

import java.util.ArrayList;

public class Hoge {
    public static void main(String[] args) {
        ArrayListt<Objectt> a = new ArrayList<Object>();
        System.out.println( a instanceof ArrayList<String> );
    }
}

%gt;javac Hoge.java Hoge.java:6: エラー: instanceofの総称型が不正です System.out.println( a instanceof ArrayList<String> ); ^


Array に対する instance of

Array の場合を見てみる。

var arrayStr: Array[String] = new Array[String](0);
var arrayRef: Array[AnyRef] = new Array[AnyRef](0);
       
println( "Array[String] is instance of Array[AnyRef]:" 
    + arrayStr.isInstanceOf[Array[AnyRef]]);
println( "Array[AnyRef] is instance of Array[String]:" 
    + arrayRef.isInstanceOf[Array[String]]);


Array[String] is instance of Array[AnyRef]:true Array[AnyRef] is instance of Array[String]:false

Array は期待通りの結果になる。

println(arrayStr.getClass().getName());
println(arrayRef.getClass().getName());


[Ljava.lang.String; [Ljava.lang.Object;

Scala の Array は Java の配列そのままなのでこういう結果になるようだ。



asInstanceOf の場合

次は、asInstanceOf を確認する。

var covariantStr : Covariantable[String] = null;
var covariantAny : Covariantable[AnyRef] = null;
covariantAny = (new Covariantable[String]).asInstanceOf[Covariantable[AnyRef]];
covariantStr = (new Covariantable[AnyRef]).asInstanceOf[Covariantable[String]];

すんなり通った。実行時エラーもなし。大丈夫か、これ。
ちょっとやってみよう。

object VarianceSample {
  def main(args : Array[String]) {
    val covarianSB = new Covariantable2[StringBuilder](new StringBuilder());
    val covariantStr = covarianSB.asInstanceOf[Covariantable2[String]];
    val value : String = covariantStr.hoge();
  }
}
class Covariantable2[+R](arg : R) {
  private val value : R = arg;
  def hoge(): R = value;
}

Exception in thread "main" java.lang.ClassCastException: scala.collection.mutable.StringBuilder cannot be cast to java.lang.String at xawa.sample.VarianceSample$.main(VarianceSample.scala:7) at xawa.sample.VarianceSample.main(VarianceSample.scala)

全然親子関係にない String と StringBuilder でも asInstanceOf は実行できる。
その結果、「 val value : String = covariantStr.hoge(); 」のところでエラーになる。



結論

結局のところ変位指定とかに関係なく、isInstanceOf も asInstanceOf も指定した型パラメータの関係性にかかわらず常にそれを無視した動作になるということのようだ。
気を付けよう。


0 件のコメント:

コメントを投稿