Lambda表达式相对于方法变量的优势是什么?

huangapple go评论66阅读模式
英文:

What is the advantage of lambda expressions over method variables?

问题

Lambda expressions(Lambda表达式)对于初学者来说可能会有点陌生,所以请容我解释。假设我有一个只有一个抽象方法的接口:

public interface IAbsFunc {
    public abstract void absFunc(int x);
}

在这种情况下,我可以将一个Lambda表达式存储在一个变量中:

public class Lambda {
    public static void main(String[] args) {
        IAbsFunc obj = (int x) -> System.out.println(x);
        obj.absFunc(3);
    }
}

如果我理解正确的话,这就像将一个方法存储在变量中。然而,这也可以使用java.lang.reflect来实现:

public class Lambda {
    public static void main(String[] args) throws Exception {
        Method obj = PrintStream.class.getDeclaredMethod("println", int.class);
        obj.invoke(System.out, 3);
    }
}

显然,我可能有些理解不够深入。那么,在什么情况下会更倾向于使用Lambda表达式呢?

英文:

I'm very new to lambda expressions, so please bear with me. Say I have an interface with a single abstract function:

public interface IAbsFunc {
    public abstract void absFunc(int x);
}

In this case, I can store a lambda expression in a variable:

public class Lambda {
    public static void main(String[] args) {
        IAbsFunc obj = (int x) -> System.out.println(x);
        obj.absFunc(3);
    }
}

If I'm understanding things right, this is like storing a method in a variable. However, this can already be done with java.lang.reflect:

public class Lambda {
    public static void main(String[] args) throws Exception {
        Method obj = PrintStream.class.getDeclaredMethod("println", int.class);
        obj.invoke(System.out, 3);
    }
}

Clearly I'm missing something here. In what situation would a lambda expression be preferable?

答案1

得分: 5

你应该了解反射 API 的用途。它是用来做那些使用其他语言特性不可能或难以实现的事情的。

官方文档中指出了一个(也是我认为最重要的)用例:

调试器需要能够检查类的私有成员。测试套件可以利用反射系统地调用在类上定义的可发现的一组 API,以确保测试套件中的代码覆盖率较高。

因此,在测试中使用反射非常有用,您可以完全访问您测试的代码。您可以访问private方法和字段,还可以更改final字段等。文档中提到了一个主要的缺点:

由于反射涉及动态解析的类型,某些 Java 虚拟机优化无法执行。因此,反射操作的性能比其非反射对应物要慢,应避免在性能敏感应用程序中频繁调用的代码部分中使用。

灵活性伴随着性能的缺陷。我在链接的文档中还可以找到更多用例和缺点。

在这种情况下,在你的代码中使用 lambda 表达式正是 lambda 表达式应该使用的方式之一(除其他用途之外)。在这里使用反射并不是反射的正确用法。你可以这样做,但最好不要这样做。

英文:

You should understand what the reflections API is for. It is made for you to do things that are not possible, or hard to do, with other language features.

An official documentation states one (and IMO the most important) use case:

> Debuggers need to be able to examine private members on classes. Test harnesses can make use of reflection to systematically call a discoverable set APIs defined on a class, to insure a high level of code coverage in a test suite.

So using reflections can be very useful in testing where you want to have full access to the code you test. You get access to private methods and fields, you can change final fields and so on. The documentation states one big disadvantage:

> Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.

The flexibility comes with performance drawbacks. There are more use cases and disadvantages that you can find in the documentation I've linked.

Using a lambda here in your case is just what a lambda is supposed to be used for (besides other things). Using reflections here is just not what reflections are made for. You can, but you shouldn't.

答案2

得分: 2

@akuzminykh 解释了为什么不应该很好地使用反射。但是,您要求关于lambda的优点:

  • 静态类型检查(与使用字符串文字相反,这是一个非常糟糕的代码味道)
  • 速度(反射很慢)
  • 无需处理ReflectiveOperationException
  • lambda的调用者不需要知道应该在哪个实例上执行方法(System.out),即Method#invoke有2个参数,而IAbsFunc#absFunc只有一个。
    • 首先不需要有一个对象来调用该方法。您可以在lambda表达式中放置多于1个语句,但是使用反射需要为包装2个语句定义另一个方法,非常冗长。
  • 可以用作方法中的回调参数
  • 您可以使用方法引用,因此可以是IAbsFunc f = System.out::println;

您应该了解函数式编程,这是一个非常有用的概念。Lambda还使用了一个称为闭包的概念,它们记住了它们在定义时所在范围中的(有效的)最终变量。

英文:

@akuzminykh explained why you should not use reflection well. However, you asked for the advantages of lambdas:

  • static type checking (as opposed to using a String literal, which is a very bad code smell)
  • speed (reflection is slow)
  • no need for handling ReflectiveOperationExceptions
  • the invoker of the lambda does not need to know the instance upon which the method should be executed (System.out), i.e. Method#invoke has 2 parameters and IAbsFunc#absFunc only one.
    • There does not need to be an object to call the method upon in the first place. You can put more than 1 statement in the lambda expression, but with reflection you would need to define another method just for wrapping 2 statements, very wordy.
  • can be used as a callback parameter in a method
  • you can use method references, so it could be IAbsFunc f = System.out::println;

You should read up on functional programming, it is a very useful concept. Lambdas also use a concept called closures, they remember the (effectively) final variables from the scope they were defined in.

huangapple
  • 本文由 发表于 2020年8月2日 07:33:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/63211007.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定