为什么@specialized注释不适用于asInstanceOf?

时间:2014-11-30 13:14:34

标签: scala generics primitive-types

据我所知,@specialized注释应为我提到的每种基本类型生成一些未装箱的代码,但这不起作用:

scala> def aaa[@specialized(Int, Double, Float, Long) T] = (5.0).doubleValue.asInstanceOf[T]
aaa: [T]=> T

scala> aaa[Int]
unrecoverable error (inside interpreter/compiler)

编译:

scala> def aaa[@specialized(Int, Double, Float, Long) T](a: T) = (5.0).doubleValue.asInstanceOf[T]
aaa: [T]=> T

scala> aaa[Int](0)
ClassCastException

但它仍然使用盒装类型asInstanceOf [T]。这显然有效:

scala> (5.0).asInstanceOf[Int]
res28: Int = 5

更新:    键入擦除和类似Writing a generic cast function Scala的答案与我的问题无关。键入擦除只是阻止编译器为泛型添加typecast字节码操作,但最终会添加 - 请参阅我的REPL中的ClassCastException(由此操作生成)

3 个答案:

答案 0 :(得分:3)

问题出在Scala REPL中 - @specialized在那里不起作用。使用scalac编译def aaa[@specialized(Int) T] = (5.0).asInstanceOf[T]会给出:

  public <T> T aaa();
    Code:
       0: ldc2_w        #15                 // double 5.0d
       3: invokestatic  #22                 // Method scala/runtime/BoxesRunTime
.boxToDouble:(D)Ljava/lang/Double;
       6: areturn

  public int aaa$mIc$sp();
    Code:
       0: ldc2_w        #15                 // double 5.0d
       3: d2i
       4: ireturn

d2i正是我所期待的。当然,一切都可以正常使用scalac(所以我不需要为每种可能的类型进行模式匹配)。所以这只是带翻译的issue

答案 1 :(得分:1)

该方法是专用的,但在单独的编译(即不同的行)下,不调用专门的方法。

在下文中,b.B.f有效,c.B.f已损坏。

$ scala -Xprint:typer,cleanup

scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

package a { object A { def aaa[@specialized(Int) T] = (5.0).doubleValue.asInstanceOf[T] }}
package b { object B { def f = a.A.aaa[Int] }}

// Exiting paste mode, now interpreting.

[[syntax trees at end of                     typer]] // <pastie>
package <empty> {
  package a {
    object A extends scala.AnyRef {
      def <init>(): a.A.type = {
        A.super.<init>();
        ()
      };
      def aaa[@specialized(scala.Int) T]: T = scala.this.Predef.double2Double(5.0).doubleValue().asInstanceOf[T]
    }
  };
  package b {
    object B extends scala.AnyRef {
      def <init>(): b.B.type = {
        B.super.<init>();
        ()
      };
      def f: Int = a.A.aaa[Int]
    }
  }
}

[[syntax trees at end of                   cleanup]] // <pastie>
package <empty> {
  package a {
    object A extends Object {
      def aaa(): Object = scala.Double.box(scala.this.Predef.double2Double(5.0).doubleValue());
      <specialized> def aaa$mIc$sp(): Int = scala.this.Predef.double2Double(5.0).doubleValue().toInt();
      def <init>(): a.A.type = {
        A.super.<init>();
        ()
      }
    }
  };
  package b {
    object B extends Object {
      def f(): Int = a.A.aaa$mIc$sp();
      def <init>(): b.B.type = {
        B.super.<init>();
        ()
      }
    }
  }
}


scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

package c { object B { def f = a.A.aaa[Int] }}

// Exiting paste mode, now interpreting.

[[syntax trees at end of                     typer]] // <pastie>
package c {
  object B extends scala.AnyRef {
    def <init>(): c.B.type = {
      B.super.<init>();
      ()
    };
    def f: Int = a.A.aaa[Int]
  }
}

[[syntax trees at end of                   cleanup]] // <pastie>
package c {
  object B extends Object {
    def f(): Int = scala.Int.unbox(a.A.aaa());
    def <init>(): c.B.type = {
      B.super.<init>();
      ()
    }
  }
}

答案 2 :(得分:-1)

也许你只需要一个更新的编译器?这是我用2.11.2得到的:

Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_72).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def aaa[@specialized(Int, Double, Float, Long) T] = (5.0).doubleValue.asInstanceOf[T]
aaa: [T]=> T

scala> aaa[Int]
java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
  at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)
  ... 33 elided

scala> aaa[Float]
java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Float
  at scala.runtime.BoxesRunTime.unboxToFloat(BoxesRunTime.java:113)
  ... 33 elided

scala> aaa[Double]
res2: Double = 5.0

scala> 5.0.asInstanceOf[Int]
res3: Int = 5

scala> 5.0.asInstanceOf[Integer]
java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
  ... 33 elided

请注意,您可以直接5.0.asInstanceOf[Int]但不能5.0.asInstanceOf[Integer]。另请注意,使用aaa[Int]获得的ClassCastException是指java.lang.Integer而不是Scala的Int类。我怀疑这里发生的是@specialized注释正在生成一个函数aaa,它具有“Int”特化,但由于类型擦除和装箱将其转换为java.lang.Integer并且没有在Double和Integer之间使用asInstanceOf进行自动转换。