lambda表达式
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它 有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
特点
- 匿名:它不像普通的方法那样有一个明确的名称:写得少而想 得多!
- 函数:为Lambda函数不像方法那样属于某个特定的类。但和方 法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递:Lambda表达式可以作为参数传递给方法或存储在变量中。
- 简洁:无需像匿名类那样写很多模板代码。
基本语法
(parameters) -> expression
或者
(parameters) -> { statements; }
使用示例
使用案例 | Lambda示例 |
---|---|
布尔表达式 | (List<String> list) -> list.isEmpty() |
创建对象 | () -> new Apple(10) |
消费一个对象 | (Apple a) -> { System.out.println(a.getWeight()); } |
从一个对象中选择/抽取 | (String s) -> s.length() |
组合两个值 | (int a, int b) -> a * b |
比较两个对象 | (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) |
哪里使用lambda?
前提
行为参数化
行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式。
让方法接受多种行为(或战 略)作为参数,并在内部使用,来完成不同的行为。
比如:
你的室友知道怎么开车去超市,再开回家。于是你可以告诉他去买一些东西,比如面包、奶酪、葡萄酒什么的。这相当于调用一goAndBuy 方法,把购物单作为参数。然而,有一天你在上班,你需要他去做一件他从来没有做过的事情:从邮局取一个包裹。现在你就需要传递给他一系列指示了:去邮局,使用单号,和工作人员说明情况,取走包裹。你可以把这些指示用电子邮件发给他,当他收到之后就可以按照指示行事了。你现在做的事情就更高级一些了,相当于一个方法: go ,它可以接受不同的新行为作为参数,然后去执行。
打车回家,当司机不知道怎么走的时候,不止要告诉目的地,还要告诉怎么走。
过滤苹果方法
private List<Apple> filterApples(List<Apple> apples, Predicate<Apple> applePerdicate){
List<Apple> filterApple = new ArrayList<>();
for(Apple apple : apples) {
if(applePerdicate.test(apple)){
filterApple.add(apple);
}
}
return filterApple;
}
调用
List<Apple> appleList = filterApples(apples, apple -> apple.getColor().equals("红色"));
appleList.forEach(System.out::println);
注意点:
- Predicate<Apple> applePerdicate 函数式接口
- apple -> apple.getColor().equals("红色") lambda表达式
- appleList.forEach(System.out::println); 方法引用
appleList.forEach(apple -> System.out.println(apple));
函数式接口
只定义一个抽象方法的接口,可包含若干个默认方法
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms
*/
package java.util.function;
import java.util.Objects;
/**
* Represents a predicate (boolean-valued function) of one argument.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
*
* @param <T> the type of the input to the predicate
*
* @since 1.8
*/
@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);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Java 8中的常用函数式接口
==Lambda表达式可以被赋给一个变量,或传递给一个接受函数式接口作为参数的方法==
使用lambda
- 环绕执行模式
资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理, 然后关闭资源。这个设置和清理阶段总是很类似,并且会围绕着执行处理的那些重要代码。
读取文件
public String processFile(Function<BufferedReader,String> bufferedReaderStringFunction) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("E:\\aa.txt"));
} catch (IOException e) {
e.printStackTrace();
}
return bufferedReaderStringFunction.apply(br);
}
@Test
public void test3() {
//读取一行
System.out.println(processFile(bufferedReader -> {
try {
return bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}));
// 读取所有行
System.out.println(processFile(bufferedReader -> bufferedReader.lines().collect(Collectors.joining(" "))));
}
- 实现runnable
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("传统的匿名类方法实现");
}
}).start();
//无参数
new Thread(() -> {
System.out.println("lamda的方式实现");
System.out.println("lamda的方式实现");
}).start();
- 事件处理
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("传统实现");
}
});
// Java 8方式:
show.addActionListener((e) -> {
System.out.println("lambda实现");
});
- 列表排序
// Java 8之前:
List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}
//java 8 1
features.forEach(n -> System.out.println(n));
features.forEach(n -> {
if (n.equals("Lambdas")) {
System.out.println("过滤的值:" + n);
}
});
features.forEach(System.out::print);
//map
Map<String, Integer> items = new HashMap<>();
items.put("A", 10);
items.put("B", 20);
items.put("C", 30);
items.put("D", 40);
items.put("E", 50);
items.put("F", 60);
for (Map.Entry<String, Integer> entry : items.entrySet()) {
System.out.println("Item : " + entry.getKey() + " Count : " + entry.getValue());
}
items.forEach((k, v) -> System.out.println("map的key=" + k + " map的value=" + v));
items.forEach((k, v) -> {
System.out.println("map的key=" + k + " map的value=" + v);
if (k.equals("F")) {
System.out.println("过滤的map value值:" + v);
}
});
//list
List<String> items1 = new ArrayList<>();
items1.add("A");
items1.add("B");
items1.add("C");
items1.add("D");
items1.add("E");
//lambda
//Output : A,B,C,D,E
items1.forEach(item1 -> System.out.println(item1));
//Output : C
items1.forEach(item1 -> {
if ("C".equals(item1)) {
System.out.println(item1);
}
});
//method reference
//Output : A,B,C,D,E
items1.forEach(System.out::println);
//Stream and filter
//Output : B
items1.stream()
.filter(s -> s.contains("B"))
.forEach(System.out::println);
- 使用lambda表达式和函数式接口Predicate
public static void filter(List list,Predicate predicate){
list.forEach(n -> {
if(predicate.test(n)){//表达式是否满足
System.out.print(n+" 1 ");
}
});
}
List<String> list = Arrays.asList("java", "scala", "c++", "haskhell", "lisp");
filter(list, str -> ((String)str).startsWith("j"));
filter(list, str -> ((String)str).length() > 4);
// 甚至可以用and()、or()和xor()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("j");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
list.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach(n -> System.out.println("过滤"+n));
- Java 8中使用lambda表达式的Map和Reduce示例
// 不使用lambda表达式为每个订单加上12%的税
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}
System.out.println("--------------");
//使用lambda表达式
costBeforeTax.stream().map(cost -> cost + .12*cost).forEach(System.out::println);
// 为每个订单加上12%的税
// 老方法:
List<Integer> costBeforeTax1 = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax1) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);
//lambda
Double total1 = costBeforeTax1.stream().map(a -> a + .12*a).reduce((a,b) -> a+b).get();
System.out.println("Total1 :"+total1);
方法引用
方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。在一些情况下, 比起使用Lambda表达式,它们似乎更易读,感觉也更自然。
例如
先前:
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
基本思想
==如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称 来调用它,而不是去描述如何调用它。事实上,方法引用就是让你根据已有的方法实现来创建 Lambda表达式。但是,显式地指明方法的名称,你的代码的可读性会更好。==
使用
==目标引用放在分隔符::前,方法的名称放在后面==
Lambda及其等效方法引用的例子
lambda | 等效的方法引用 |
---|---|
(Apple a) -> a.getWeight() | Apple::getWeight |
() -> Thread.currentThread().dumpStack() | Thread.currentThread()::dumpStack |
(str, i) -> str.substring(i) | String::substring |
(String s) -> System.out.println(s) | System.out::println |
构建方法引用的三种方式
- 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)
- 指向任意类型实例方法的方法引用(例如String 的 length 方法,写作 String::length)。
其思想是:引用一个对象的方法,而这个对象本身是Lambda的一个参数。例如,Lambda 表达式(String s) -> s.toUppeCase()可以写作String::toUpperCase。 - 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction 用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensive- Transaction::getValue)。 其实说在Lambda中调用一个已经存在的外部对象中的方法。
字符串列表排序
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
//方法引用
str.sort(String::compareToIgnoreCase);
构造函数引用
对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用: ClassName::new。它的功能与指向静态方法的引用类似。
例如,假设有一个构造函数没有参数。 它适合Supplier的签名() -> Apple。你可以这样做:
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
//等价于
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
如果你的构造函数的签名是Apple(Integer weight),那么它就适合Function接口的签 名,于是你可以这样写:
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
//等价于
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);
//一个由Integer构成的List中的每个元素都通过我们前面定义的类似的 map方法传递给了Apple的构造函数,得到了一个具有不同重量苹果的List:
List<Integer> weights = Arrays.asList(7, 3, 4, 10);
List<Apple> apples = map(weights, Apple::new);
public static List<Apple> map(List<Integer> list, Function<Integer, Apple> f){
List<Apple> result = new ArrayList<>();
for(Integer e: list){
result.add(f.apply(e));
}
return result;
}
如果你有一个具有两个参数的构造函数Apple(String color, Integer weight),那么 它就适合BiFunction接口的签名,于是你可以这样写:
BiFunction<String, Integer, Apple> c3 = Apple::new;
Apple c3 = c3.apply("green", 110);
//等价于
BiFunction<String, Integer, Apple> c3 =(color, weight) -> new Apple(color, weight);
Apple c3 = c3.apply("green", 110);
最终
类似于inventory.sort(comparing(Apple::getWeight));
,集合着行为参数化、匿名类、Lambda 表达式和方法引用等所有特点。