为什么使用lambda而不是普通方法?

fslejnso  于 2021-07-03  发布在  Java
关注(0)|答案(3)|浏览(311)

所以假设我的应用程序做了很多重复的工作,例如,假设我的应用程序检查了很多不同的列表是否为空。有两种方法可以实现这个功能-(可能还有其他方法,但由于我的目标是理解这两种方法的区别,而不是功能本身,所以我们来这里)
方法1-传统方法

public boolean isEmptyOrNull(List list)
{
return list != null && !list.isEmpty();
}

方法2-lambda方法
假设我们已经创建了一个函数接口,类名demo和boolean isemptyornull作为函数。

Demo var = list -> list != null && !list.isEmpty();

所以每次我想检查一个列表时,我可以使用方法1或者方法2 isEmptyOrNull(myList) 或者 var.isEmptyOrNull(myList) 分别。
我的问题是为什么我应该使用方法1而不是方法2,反之亦然。为什么我应该选择一种方法而不是另一种方法,这是性能方面还是其他方面!?

wmtdaxz3

wmtdaxz31#

添加lambda是为了引入java中的函数式编程。它可以作为实现单一方法接口(功能接口)的简写。在您提供的上述示例中,没有什么优势。但lambdas在以下场景中可能有用:
在lambdas之前

public Interface Calc{
    int doCalc(int a, int b);
}

public class MyClass{
    public void main(String[] args){
        Calc x = new Calc() {

            @Override
            public int doCalc(int a, int b) {
                return a + b;
            }
        };
        System.out.println(x.doCalc(2, 3));
    }
}

但对于lambdas,这可以简化为

public class MyClass{
    public void main(String[] args){
        BiFunction<Integer, Integer, Integer> doCalc= (a, b) -> a + b;
        System.out.println(doCalc.apply(2, 3));
    }
}

这对于实现事件监听器(在android中)特别有用,在这种情况下,有许多接口作为api的一部分提供,它们的方法如下 onClick 在这种情况下,lambda可能有助于减少代码。
也与 Java 8 ,流被引入,lambda可以被传递来过滤/Map流元素。流允许比传统流更可读的代码 for loop / if-else 在大多数情况下。

lc8prwob

lc8prwob2#

哦,从哪里开始。

你对空是什么的看法被打破了。 isEmptyOrNull 是密码的味道。你不应该用这种方法。 null 是一个替代值,它必然意味着“未初始化”,因为它内置于java本身:任何未显式设置的字段都将被初始化 null . 然而,它在api中非常常见,甚至在 java.* API,那个 null 也可以是“找不到”的意思(例如当你打电话时) map.get(someKeyNotInTheMap) ),有时也“与此上下文无关”,例如向引导类请求 ClassLoader .

一般来说,它并不意味着“空”。这是因为有一个非常好的非空值,它可以完美地表示空。对于弦, "" 是空字符串,所以使用它,不要随意返回 null 相反。对于列表,是一个空列表(尽可能容易制作) List.of() )是应该用于空列表的。
假设 null 在语义上与…的意思完全相同 List.of() 要么是不必要的(该列表的源不会首先返回null,从而使null检查变得不必要),要么更糟,将隐藏错误:您错误地将“uninitialized”解释为“empty”,这是一个很好的方法来产生一个bug,并使您的应用程序无所事事,从而很难找到bug。最好是一个bug大声宣布它的存在,并准确地指向代码中该bug存在的地方,这就是为什么你想要一个异常,而不是一个“什么都不做,当那是不正确的”风格的bug。

lambda代码无法编译

除非 Demo 是一个具有 boolean isEmptyOrNull(List list); ,即。

区别

关键的区别在于lambda表示可以引用的方法。可以将lambda本身作为参数传递。
例如, java.util.TreeSet 是set的一个实现,它通过使用树按排序顺序存储放入其中的所有元素。这就像建立一个电话簿:要把“伯恩斯坦女士”放进电话簿,你打开电话簿中间的部分,在那里检查姓名,如果是“在”伯恩斯坦上面,看前半部分的中间部分。一直走,直到你找到伯恩斯坦应该被插入的地方;即使在一个有一百万个号码的电话簿中,这也只需要大约20个步骤,这就是为什么treeset很快,即使你在里面放了很多东西。
treeset需要做的一件事是一个比较函数:“给定‘maidstone’和‘bernstein’的名字,哪一个应该在电话簿后面列出”?这就是全部。如果您有这个函数,那么treeset就可以完成它的工作,而不管您存储在其中的对象是什么类型的。
假设你想做一个电话簿,首先按姓名的长度排序,然后按字母顺序排序。
这要求您传递一个函数,该函数规定两个名称中的哪一个在另一个之后。lambdas使这变得简单:

Comparator<String> decider = (a, b) -> {
    if (a.length() < b.length()) return -1;
    if (a.length() > b.length()) return +1;
    return a.compareTo(b);
};

SortedSet<String> phonebook = new TreeSet<>(decider);

现在试着不用lambdas来写这个。您将不能这样做,因为您不能使用这样的方法名。这不起作用:

public void decider(String a, String b) {
    if (a.length() < b.length()) return -1;
    if (a.length() > b.length()) return +1;
    return a.compareTo(b);
}

public SortedSet<String> makeLengthBook() {
    return new TreeSet<String>(decider);
}

有很多原因不起作用,但从语言设计的Angular 来看:因为在java中可以有一个名为 decider ,以及一个名为 decider . 你可以写 this::decider 那就行了-那只是糖的语法 (a, b) -> this.decider(a, b); 你应该尽一切可能使用它。

bq9c1y66

bq9c1y663#

你的例子没有什么好处。lamda通常用于对象流中,例如通过定义“adhoc函数”(lambda就是这样)来Map或过滤。
示例:您有一个名为 allStrings 要筛选的

List<String> longStrings = allStrings.stream()
    .filter(s -> s.length() > 5) // only keep strings longer than 5
    .collect(Collectors.toList()); // collect stream in a new list

相关问题