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

被折叠的 条评论
为什么被折叠?



