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

scala – 部分应用和返回函数之间有区别吗?

发布时间:2020-12-16 18:34:17 所属栏目:安全 来源:网络整理
导读:在引擎盖方面:堆栈/堆分配,垃圾收集,资源和性能,以下三个有什么区别: def Do1(a:String) = { (b:String) = { println(a,b) }}def Do2(a:String)(b:String) = { println(a,b) }def Do3(a:String,b:String) = { println(a,b) }Do1("a")("b")Do2("a")("b")(Do
在引擎盖方面:堆栈/堆分配,垃圾收集,资源和性能,以下三个有什么区别:

def Do1(a:String) = { (b:String) => { println(a,b) }}
def Do2(a:String)(b:String) = { println(a,b) }
def Do3(a:String,b:String) = { println(a,b) }

Do1("a")("b")
Do2("a")("b")
(Do3("a",_:String))("b")

除了声明中每个采取和返回多少参数的明显表面差异

解决方法

反编译以下类(请注意与您的问题相比对Do2的额外调用):

class Test {
  def Do1(a: String) = { (b: String) => { println(a,b) } }
  def Do2(a: String)(b: String) = { println(a,b) }
  def Do3(a: String,b: String) = { println(a,b) }

  Do1("a")("b")
  Do2("a")("b")
  (Do2("a") _)("b")
  (Do3("a",_: String))("b")
}

产生这个纯Java代码:

public class Test {
    public Function1<String,BoxedUnit> Do1(final String a) {
        new AbstractFunction1() {
            public final void apply(String b) {
                Predef..MODULE$.println(new Tuple2(a,b));
            }
        };
    }

    public void Do2(String a,String b) {
        Predef..MODULE$.println(new Tuple2(a,b));
    }

    public void Do3(String a,b));
    }

    public Test() {
        Do1("a").apply("b");
        Do2("a","b");
        new AbstractFunction1() {
            public final void apply(String b) {
                Test.this.Do2("a",b);
            }
        }.apply("b");
        new AbstractFunction1() {
            public final void apply(String x$1) {
                Test.this.Do3("a",x$1);
            }
        }.apply("b");
    }
}

(此代码无法编译,但足以进行分析)

让我们逐个看一下(每个列表中的Scala& Java):

def Do1(a: String) = { (b: String) => { println(a,b) } }

public Function1<String,BoxedUnit> Do1(final String a) {
    new AbstractFunction1() {
        public final void apply(String b) {
            Predef.MODULE$.println(new Tuple2(a,b));
        }
    };
}

无论如何调用Do1,都会创建一个新的Function对象.

def Do2(a: String)(b: String) = { println(a,b) }

public void Do2(String a,String b) {
    Predef.MODULE$.println(new Tuple2(a,b));
}

def Do3(a: String,b) }

public void Do3(String a,b));
}

Do2和Do3编译为相同的字节码.区别仅在于@ScalaSignature注释.

Do1("a")("b")

Do1("a").apply("b");

Do1是直接的:立即应用返回的函数.

Do2("a")("b")

Do2("a","b");

使用Do2,编译器会发现这不是部分应用程序,并将其编译为单个方法调用.

(Do2("a") _)("b")

new AbstractFunction1() {
    public final void apply(String b) {
        Test.this.Do2("a",b);
    }
}.apply("b");

(Do3("a",_: String))("b")

new AbstractFunction1() {
    public final void apply(String x$1) {
        Test.this.Do3("a",x$1);
    }
}.apply("b");

这里,首先部分应用Do2和Do3,然后立即应用返回的函数.

结论:

我想说Do2和Do3在生成的字节码中大部分都是等价的.完整的应用程序会导致简单,廉价的方法调用.部分应用程序在调用者处生成匿名函数类.您使用的变体主要取决于您尝试沟通的意图.

Do1总是创建一个立即的函数对象,但是在被调用的代码中这样做.如果您希望对该函数进行大量部分应用,那么使用此变体将减少您的代码大小,并可能更早地触发JIT-Compiler,因为更频繁地调用相同的代码.完整的应用程序将会更慢,至少在JIT-Compiler内联之前并且随后消除了各个呼叫站点的对象创建.我不是这方面的专家,所以我不知道你是否可以期待那种优化.对于纯粹的功能,我最好的猜测就是你可以.

(编辑:李大同)

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

    推荐文章
      热点阅读