5

谁能解释为什么下面的 Scala 代码片段:

def convertRefToVal(obj: Any): Int = {
  if (obj.isInstanceOf[java.lang.Integer]) obj.asInstanceOf[Int]
  else -1
}

convertRefToVal(42).getClass()

打印java.lang.Class[Int] = int,而这个:

def convertRefToVal(obj: Any): AnyVal = {
  if (obj.isInstanceOf[java.lang.Integer]) obj.asInstanceOf[Int]
  else -1
}

convertRefToVal(42).getClass()

生产java.lang.Class[_] = class java.lang.Integer?

除了返回类型(Int 与 AnyVal)之外,这些方法是相同的。

所以第一个例子返回一个 Int 值类型,而在第二个例子中,我得到一个 java.lang.Integer 引用类型作为结果。看起来自动装箱正在发生,但我不希望看到这种情况,因为第二个版本将 AnyVal 指定为其返回类型?

(我使用的是 Scala 2.10.2)

4

3 回答 3

5

实际上,自动装箱在两个版本中都应用了,在函数的入口点,可能是因为obj需要Any. 但有趣的是当您考虑类型时:

def convertRefToVal(obj: Any): Int = {
  println(obj.isInstanceOf[java.lang.Integer])
  println(obj.isInstanceOf[Int])
  println(obj.getClass()) 
  if (obj.isInstanceOf[java.lang.Integer]) obj.asInstanceOf[Int]
  else -1
}
convertRefToVal(42)

这打印:

true
true
class java.lang.Integer

所以一个问题是在任何情况下java.lang.Integer都被认为是 的一个实例。Int

无论如何,看起来 Scala 对根据返回类型从包装器“转换”到原语有特定的支持。我将尝试找到原因的答案并在我的问题中进行编辑。

编辑:其他人可能会提供一个历史原因,但这是一个事实。这是javap两个函数的打印内容:

public int convertRefToVal(java.lang.Object); //first version
public java.lang.Object convertRefToVal1(java.lang.Object); //second version

因此,如您所见,从长远来看,AnyVal映射到java.lang.Object。事实上,这两个函数之间的区别在于,虽然两个函数都取消了先前自动装箱的参数,但第二个函数又将它装箱了。

为了演示,这是一个示例类:

package stuff

object PrimTest {

  def convertRefToVal(obj: Any): Int = {
    if (obj.isInstanceOf[java.lang.Integer]) obj.asInstanceOf[Int]
    else -1
  }

  def convertRefToVal1(obj: Any): AnyVal = {
    if (obj.isInstanceOf[java.lang.Integer]) obj.asInstanceOf[Int]
    else -1
  }

  def main(args: Array[String]): Unit = {
    new java.lang.Integer(42).asInstanceOf[Int] //added for isolating the cast example
  }

}

这是java -p -v输出:

  Compiled from "PrimTest.scala"
public final class stuff.PrimTest$
  SourceFile: "PrimTest.scala"
    Scala: length = 0x0

  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
   #1 = Utf8               stuff/PrimTest$
   #2 = Class              #1             //  stuff/PrimTest$
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             //  java/lang/Object
   #5 = Utf8               PrimTest.scala
   #6 = Utf8               MODULE$
   #7 = Utf8               Lstuff/PrimTest$;
   #8 = Utf8               <clinit>
   #9 = Utf8               ()V
  #10 = Utf8               <init>
  #11 = NameAndType        #10:#9         //  "<init>":()V
  #12 = Methodref          #2.#11         //  stuff/PrimTest$."<init>":()V
  #13 = Utf8               convertRefToVal
  #14 = Utf8               (Ljava/lang/Object;)I
  #15 = Utf8               java/lang/Integer
  #16 = Class              #15            //  java/lang/Integer
  #17 = Utf8               scala/runtime/BoxesRunTime
  #18 = Class              #17            //  scala/runtime/BoxesRunTime
  #19 = Utf8               unboxToInt
  #20 = NameAndType        #19:#14        //  unboxToInt:(Ljava/lang/Object;)I
  #21 = Methodref          #18.#20        //  scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
  #22 = Utf8               this
  #23 = Utf8               obj
  #24 = Utf8               Ljava/lang/Object;
  #25 = Utf8               convertRefToVal1
  #26 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;
  #27 = Utf8               boxToInteger
  #28 = Utf8               (I)Ljava/lang/Integer;
  #29 = NameAndType        #27:#28        //  boxToInteger:(I)Ljava/lang/Integer;
  #30 = Methodref          #18.#29        //  scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
  #31 = Utf8               main
  #32 = Utf8               ([Ljava/lang/String;)V
  #33 = Utf8               (I)V
  #34 = NameAndType        #10:#33        //  "<init>":(I)V
  #35 = Methodref          #16.#34        //  java/lang/Integer."<init>":(I)V
  #36 = Utf8               args
  #37 = Utf8               [Ljava/lang/String;
  #38 = Methodref          #4.#11         //  java/lang/Object."<init>":()V
  #39 = NameAndType        #6:#7          //  MODULE$:Lstuff/PrimTest$;
  #40 = Fieldref           #2.#39         //  stuff/PrimTest$.MODULE$:Lstuff/PrimTest$;
  #41 = Utf8               Code
  #42 = Utf8               LocalVariableTable
  #43 = Utf8               LineNumberTable
  #44 = Utf8               StackMapTable
  #45 = Utf8               SourceFile
  #46 = Utf8               Scala
{
  public static final stuff.PrimTest$ MODULE$;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public static {};
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: new           #2                  // class stuff/PrimTest$
         3: invokespecial #12                 // Method "<init>":()V
         6: return        

  public int convertRefToVal(java.lang.Object);
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_1       
         1: instanceof    #16                 // class java/lang/Integer
         4: ifeq          14
         7: aload_1       
         8: invokestatic  #21                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
        11: goto          15
        14: iconst_m1     
        15: ireturn       
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      16     0  this   Lstuff/PrimTest$;
               0      16     1   obj   Ljava/lang/Object;
      LineNumberTable:
        line 6: 0
        line 7: 14
        line 6: 15
      StackMapTable: number_of_entries = 2
           frame_type = 14 /* same */
           frame_type = 64 /* same_locals_1_stack_item */
          stack = [ int ]


  public java.lang.Object convertRefToVal1(java.lang.Object);
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_1       
         1: instanceof    #16                 // class java/lang/Integer
         4: ifeq          17
         7: aload_1       
         8: invokestatic  #21                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
        11: invokestatic  #30                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
        14: goto          21
        17: iconst_m1     
        18: invokestatic  #30                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
        21: areturn       
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      22     0  this   Lstuff/PrimTest$;
               0      22     1   obj   Ljava/lang/Object;
      LineNumberTable:
        line 11: 0
        line 12: 17
        line 11: 21
      StackMapTable: number_of_entries = 2
           frame_type = 17 /* same */
           frame_type = 67 /* same_locals_1_stack_item */
          stack = [ class java/lang/Integer ]


  public void main(java.lang.String[]);
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: new           #16                 // class java/lang/Integer
         3: dup           
         4: bipush        42
         6: invokespecial #35                 // Method java/lang/Integer."<init>":(I)V
         9: invokestatic  #21                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
        12: pop           
        13: return        
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      14     0  this   Lstuff/PrimTest$;
               0      14     1  args   [Ljava/lang/String;
      LineNumberTable:
        line 16: 0

  private stuff.PrimTest$();
    flags: ACC_PRIVATE
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #38                 // Method java/lang/Object."<init>":()V
         4: aload_0       
         5: putstatic     #40                 // Field MODULE$:Lstuff/PrimTest$;
         8: return        
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   Lstuff/PrimTest$;
      LineNumberTable:
        line 3: 0
}

注意调用的用法BoxesRunTime,它实际上是一个Java类 BTW。这表明编译器中可能有一些特定的代码添加了这些调用。

于 2013-07-22T19:00:36.450 回答
2

如您所知,基元不是对象。对于具有公共超类型的值类型,它们需要是对象,因此将装箱应用于第二个版本。

于 2013-07-22T18:46:46.950 回答
1

当您定义函数返回类型时Int,会发生隐式转换implicit def Integer2int(x: jl.Integer): Int

您可以在中列出隐式转换sbt console

scala> :implicits -v

...
implicit def Integer2int(x: jl.Integer): Int
...

这里还有一个示例控制台脚本:

scala> val ji: java.lang.Integer = 1
ji: java.lang.Integer = 1

scala> val si: Int = ji  // here the implicit conversion is taking place
si: Int = 1

scala> ji.getClass()
res4: java.lang.Class[_ <: java.lang.Integer] = class java.lang.Integer

scala> si.getClass()
res5: java.lang.Class[Int] = int

scala> Integer2int(ji)
res6: Int = 1

scala> Integer2int(ji).getClass // calling it explicitly
res7: java.lang.Class[Int] = int

在docs.scala-lang.org上查看有关隐式转换的更多信息。

于 2013-07-22T19:33:12.300 回答