加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

在Scala reduceLeft实现中的0.asInstanceOf [B]发生了什么

发布时间:2020-12-16 09:45:35 所属栏目:安全 来源:网络整理
导读:以下是Scala的 TraversableOnce特征的reduceLeft方法的来源。读取var acc:B = 0.asInstanceOf [B]的行发生了什么? 对我来说,如果我将这个字符串列在列表(“a”,“b”,“c”)这样的列表中,那么会导致类似于0.asInstanceOf [String]的内容。但是,如果我
以下是Scala的 TraversableOnce特征的reduceLeft方法的来源。读取var acc:B = 0.asInstanceOf [B]的行发生了什么?

对我来说,如果我将这个字符串列在列表(“a”,“b”,“c”)这样的列表中,那么会导致类似于0.asInstanceOf [String]的内容。但是,如果我直接尝试,在运行时,0.asInstanceOf [String]会抛出ClassCastException。

该行发生了什么,为什么不同于在Strings列表上调用时直接调用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对象中的第一个元素覆盖该值。

解决方法

那么在运行时没有发生ClassCastException的原因是因为:

>编译器删除它从未使用的初始化推理(我怀疑这一点)
> B是erased到Object(或AnyRef),因此转换真的是0.asInstanceOf [Object]

您可以通过检查字节码来测试第一种可能性,但它似乎是一个无意义的代码段。如果没有消除这种情况,那么每次调用该方法时都会有一个不必要的开销来控制Int(尽管不是一个对象创建 – 见下文)!

了解编译器生成的内容:1

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 

}

做那个你会的!

了解编译器生成的内容:2

另一种可能性是把它放在一个源文件和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的boxed值被缓存。

您可以通过将上述打印命令中的编译器阶段更改为“擦除”来检查已擦除的行。嘿presto:

var acc: java.lang.Object = scala.Int.box(0).$asInstanceOf[java.lang.Object]();

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读