【问题标题】:Why @specialized annotation doesn't work for asInstanceOf?为什么@specialized 注释不适用于 asInstanceOf?
【发布时间】:2014-11-30 13:14:33
【问题描述】:

据我了解,@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(由此操作生成)

【问题讨论】:

    标签: scala generics primitive-types


    【解决方案1】:

    问题出在 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

    【讨论】:

    【解决方案2】:

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

    在下面,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>();
          ()
        }
      }
    }
    

    【讨论】:

    • 所以如果我从另一个单元调用专门的方法 - 即使使用 scalac 也不起作用?
    • @dk14 REPL 是一个常驻编译器,因此该错误可能源自那里。也许它甚至没有看到专门的签名,只有原始符号;这只是一个猜测。
    【解决方案3】:

    也许您只需要更新的编译器?这是我在 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 并且没有自动转换asInstanceOf 在 Double 和 Integer 之间。

    【讨论】:

    • 我使用最新的编译器,但可能是 REPL 特有的问题。无论如何,问题是即使在函数内部使用@specialized 注释类型仍然是 boxed (整数而不是 Int)。编译时没有类型擦除 - 比每个生成的函数都明确知道类型 - 所以没有理由不直接指定 Int 而不是装箱的 Integer。
    猜你喜欢
    • 2012-12-27
    • 2020-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    • 2021-06-03
    • 1970-01-01
    相关资源
    最近更新 更多