Java8 知识整理 之 Lambda表达式 二

本文详细介绍了Java8中的Lambda表达式及其应用,包括Lambda的基本语法、函数式接口的使用,如Predicate、Consumer、Function和Supplier。通过方法引用的讲解,展示了如何简化代码并提升可读性。文章还强调了Lambda表达式在函数式编程中的重要性,以及它们如何与Java8提供的内置函数式接口相互作用。

上一篇 Java8 知识整理 之 Lambda表达式 一

Java8 知识整理 之 Lambda表达式 二

上一篇我们讲解了Lambda的基本使用,接下来我们进一步的学习Lambda。

1. Lambda 语法基础

1.1 案例演示

在这里插入图片描述

Lambda表达式由参数、箭头和主体组成。

比如上一章的例子:

ConditionalFilter<String> filter =  new ConditionalFilter<String>() {
    @Override
    public boolean test(String s) {
        return "Lambda".equalsIgnoreCase(s);
    }
};

之后,用了Lambda表达式:

s -> "Lambda".equalsIgnoreCase(s);

不得不承认,代码简洁了很多。

1.2 Lambda语法

java8中有效的表达式如下:

定义接口的方法Lambda表达式含义
public void m();() -> System.out.println(“Lambda”);没有参数和返回值
public boolean test(String s);s -> s.length()>0有一个参数但没有返回值
public void cul(Integer x,Integer y);(o1,o2) -> System.out.println(o1+o2);多个参数但没有返回值
public Integer cul(Integer x,Integer y);(o1,o2) -> o1+o2;多个参数但有返回值
public String [] splitByComma(String s);s -> { String[] splitByComma = s.split(","); return splitByComma; };主体包含多条语句

使用案例:

在这里插入图片描述

2. 在哪里以及如何使用Lambda

2.1 函数式接口

我们先来看一个接口:

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

这个接口就是一个函数式接口。为什么呢?因为Predicate仅仅定义了一个抽象方法 。一言以蔽之,函数式接口就是只定义一个抽象方法的接口。

Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现的实例)。你用匿名内部类也可以完成同样的事情,只不过比较笨拙:需要提供一个实现,然后再直接内联将它实例化。

Lambda 提供该接口的实现:

 Predicate<String> p =  s -> s.length() > 0;
2.2 四大函数式接口

Java 8的库设计师帮你在java.util.function包中引入了几个新的函数式接口,接下来我们来介绍。

2.2.1 Predicate

java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。这恰恰和你先前创建的一样,现在就可以直接使用了。在你需要表示一个涉及类型T的布尔表达式时,就可以使用这个接口。比如,你可以定义一个接受String对象的Lambda表达式。

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t); 
}

2.2.2 Consumer

java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。比如,你可以用它来创建一个forEach方法,接受一个Integers的列表,并对其中每个元素执行操作。

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

}

2.2.3 Function

java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射到输出,就可以使用这个接口。

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

}

2.2.4 Supplier

java.util.function.Supplier接口定义了一个叫作get的方法, 它可以返回一个泛型T的对象。如果你需要定义一个Lambda,只要求它返回一个值,可以使用该接口。

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

3. 方法引用

方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。在一些情况下,比起使用Lambda表达式,它们似乎更易读,感觉也更自然。

使用一个排序的例子:

①、比如我们定义个Integer 类型的 List,并保存一些整数。

 List<Integer> list = Arrays.asList(1,3,2,8,4,5);

②、调用 list的 sort的方法;

我们先来看一下 sort方法的签名。

 public void sort(Comparator<? super E> c);

再看一下 Comparator<? super E> c 接口。

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
}

Comparator 是一个 函数式接口,那么我们可以使用Lambda表达式,作为sort方法的参数。

orderList.sort((Integer o1,Integer o2) -> o1.compareTo(o2));

完成的代码:

public class OrderLambda {
    public static void main(String[] args){
        List<Integer> orderList = Arrays.asList(1,3,2,8,4,5);
        orderList.sort((Integer o1,Integer o2) -> o1.compareTo(o2));
        System.out.println(orderList);
    }
}

③、我们使用方法引用修改上面的代码

//  orderList.sort((Integer o1,Integer o2) -> o1.compareTo(o2)); 
orderList.sort(Integer::compareTo);
3.1 如何构建方法引用

方法引用主要有三类。

①、指向静态方法的方法引用

假设定义了一个 将String转化为 Integer的接口。

@FunctionalInterface
public interface StringToIntegerConversion {
       Integer  converter(String s);
}

而 Integer.valueOf(s) 方法正好实现了这个功,那么我们可以这么做

StringToIntegerConversion  stc =  s -> Integer.valueOf(s);

当接口方法的签名和 类的静态方法签名相同时 可以使用方法引用

 StringToIntegerConversion  stc =  Integer::valueOf;

②、指向任意类型实例方法的方法引用(例如String的length方法,写作String::length)。

③、指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)。

在这里插入图片描述

3.2 构造函数引用

对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用:ClassName::new。它的功能与指向静态方法的引用类似。

例如:

Supplier<User> c1 = User::new;    //构造函数引用指向默认的User()构造函数
User a1 = c1.get();    //调用Supplier的get方法将产生一个新的User

这等价于

Supplier<User> c1 = new User();    //利用默认构造函数创建User的Lambda表达式
User a1 = c1.get();    //调用Supplier的get方法将产生一个新的User

如果你的构造函数的签名是User(Integer old),那么它就适合Function接口的签名,于是你可以这样写:

Function<Integer, User> c2 = User::new;   //指向User(Integer ol)的构造函数引用
User a2 = c2.apply(110);    //调用该Function函数的apply方法,并给出要求的重量,将产生一个User

这就等价于:

Function<Integer, User> c2 = (old) -> new Apple(old);//用要求的年龄创建一个User的Lambda表达式
User a2 = c2.apply(70);调用该Function函数的apply方法,并给出要求的年龄,将产生一个新的User对象

4.小结

  • Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常的列表。
  • Lambda表达式让你可以简洁地传递代码。
  • 函数式接口就是仅仅声明了一个抽象方法的接口。
  • 只有在接受函数式接口的地方才可以使用Lambda表达式。
  • Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
  • Java 8自带一些常用的函数式接口,放在java.util.function包里,包括Predicate、Function<T,R>、Supplier、Consumer和BinaryOperator,如表3-2所述。
  • Lambda表达式所需要代表的类型称为目标类型。
  • 方法引用让你重复使用现有的方法实现并直接传递它们。
  • Comparator、Predicate和Function等函数式接口都有几个可以用来结合Lambda表达式的默认方法。

象方法的接口。

  • 只有在接受函数式接口的地方才可以使用Lambda表达式。
  • Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
  • Java 8自带一些常用的函数式接口,放在java.util.function包里,包括Predicate、Function<T,R>、Supplier、Consumer和BinaryOperator,如表3-2所述。
  • Lambda表达式所需要代表的类型称为目标类型。
  • 方法引用让你重复使用现有的方法实现并直接传递它们。
  • Comparator、Predicate和Function等函数式接口都有几个可以用来结合Lambda表达式的默认方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值