以下是Scala的TraversableOnce特征的reduceLeft方法的来源。读取var acc: B = 0.asInstanceOf[B]
的行会发生什么?
对我来说,似乎如果我在一个字符串列表上调用它,例如List("a", "b", "c")
,这会产生类似0.asInstanceOf[String]
的内容。但是,如果我直接尝试,0.asInstanceOf[String]
会在运行时抛出ClassCastException
。
该行发生了什么,为什么它与在字符串列表中调用时直接调用0.asInstanceOf[String]
不同?
def reduceLeft[B >: A](op: (B, A) => B): B = {
if (isEmpty)
throw new UnsupportedOperationException("empty.reduceLeft")
var first = true
var acc: B = 0.asInstanceOf[B]
for (x <- self) {
if (first) {
acc = x
first = false
}
else acc = op(acc, x)
}
acc
}
奖金问题:为什么acc
甚至被初始化为该值?看起来for
循环的第一次迭代中的代码只会用TraversableOnce
对象中的第一个元素覆盖该值。
答案 0 :(得分:14)
嗯,运行时没有ClassCastException
失败的原因是因为:
Object
(或AnyRef
),因此演员阵容真的是0.asInstanceOf[Object]
你可以通过检查字节码测试第一种可能性,但它似乎是一个毫无意义的代码。如果它没有被省略,那么每次调用该方法时都会有Int
的不必要的开销(虽然不是对象创建 - 见下文)!
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
Type in expressions to have them evaluated.
Type :help for more information.
scala> trait X[A] {
| def foreach[U](f: A => U): U
| def isEmpty = {
| var b = false
| foreach(_ => b = true)
| !b
| }
| def reduceLeft[B >: A](op: (B, A) => B): B = {
| if (isEmpty) sys.error("Bad")
| var first = true
| var acc: B = 0.asInstanceOf[B]
| for (x <- this) {
| if (first) { acc = x; first = false } else acc = op(acc, x)
| }
| acc
| }
| }
defined trait X
现在:
scala> :javap -v X
Compiled from "<console>"
public interface X extends scala.ScalaObject
SourceFile: "<console>"
Scala: length = 0x
Signature: length = 0x2
00 0D
InnerClass:
public abstract #19= #16 of #18; //X=class X of class
public final #21; //class X$$anonfun$isEmpty$1
public final #23; //class X$$anonfun$reduceLeft$1
minor version: 0
major version: 49
Constant pool:
const #1 = Asciz SourceFile;
const #2 = Asciz <console>;
const #3 = Asciz foreach;
const #4 = Asciz (Lscala/Function1;)Ljava/lang/Object;;
const #5 = Asciz <U:Ljava/lang/Object;>(Lscala/Function1<TA;TU;>;)TU;;
const #6 = Asciz Signature;
const #7 = Asciz isEmpty;
const #8 = Asciz ()Z;
const #9 = Asciz reduceLeft;
const #10 = Asciz (Lscala/Function2;)Ljava/lang/Object;;
const #11 = Asciz <B:Ljava/lang/Object;>(Lscala/Function2<TB;TA;TB;>;)TB;;
const #12 = Asciz Scala;
const #13 = Asciz <A:Ljava/lang/Object;>Ljava/lang/Object;Lscala/ScalaObject;;
const #14 = Asciz InnerClasses;
const #15 = Asciz X;
const #16 = class #15; // X
const #17 = Asciz ;
const #18 = class #17; //
const #19 = Asciz X;
const #20 = Asciz X$$anonfun$isEmpty$1;
const #21 = class #20; // X$$anonfun$isEmpty$1
const #22 = Asciz X$$anonfun$reduceLeft$1;
const #23 = class #22; // X$$anonfun$reduceLeft$1
const #24 = Asciz java/lang/Object;
const #25 = class #24; // java/lang/Object
const #26 = Asciz scala/ScalaObject;
const #27 = class #26; // scala/ScalaObject
{
public abstract java.lang.Object foreach(scala.Function1);
Signature: length = 0x2
00 05
public abstract boolean isEmpty();
public abstract java.lang.Object reduceLeft(scala.Function2);
Signature: length = 0x2
00 0B
}
准备好你的意思!
另一种可能性是将其放在源文件中compile it, printing out the intermediate code phase:
./scalac -Xprint:icode X.scala
然后你得到......
def reduceLeft($this: X, op$1: Function2): java.lang.Object = {
if ($this.isEmpty())
scala.sys.`package`.error("Bad")
else
();
var first$1: scala.runtime.BooleanRef = new scala.runtime.BooleanRef(true);
var acc$1: scala.runtime.ObjectRef = new scala.runtime.ObjectRef(scala.Int.box(0));
$this.foreach({
(new anonymous class X$$anonfun$reduceLeft$1($this, op$1, first$1, acc$1): Function1)
});
acc.elem
};
它看起来非常像在那里不必要的拳击!有一点是,这不会涉及对象创建,只涉及查找,因为缓存了-127到127的装箱值。
您可以通过将上述打印命令中的编译器阶段更改为“擦除”来检查已擦除的行。嘿presto:
var acc: java.lang.Object = scala.Int.box(0).$asInstanceOf[java.lang.Object]();
答案 1 :(得分:5)
可以通过尝试提出替代方案来推断奖金问题的答案。累加器是B型。你会分配给它什么B? (有一个更好的答案,即null.asInstanceOf [B]这是通常所做的,但我认为这会留下你的问题。)