以下文章来源于草捏子 ,作者草捏子
专注后端技术,坚持原创。
(parameters) -> expression
(parameters) -> { statements; }
() -> {}
() -> "Apple"
() -> { return "Apple"; }
(Integer i) -> return "Apple" + i
(String s) -> { "Apple"; }
return
则为表达式风格;(5)非有效,"Apple"
是一个字符串表达式,不是一个语句,加上return
,或者去掉分号和花括号。public interface Comparator<T> {
int compare(T o1, T o2);
}
public interface Runnable {
void run();
}
public interface Callable<V> {
V call() throws Exception;
}
(T, T) -> int
,两个泛型T类型的入参,返回int类型(Apple a1, Apple a2) -> a1.getWeight - a2.getWeight
() -> void
,无入参,无返回值() -> { System.out.println("Hi"); }
() -> V
,无入参,返回一个泛型V类型的对象() -> new Apple()
new Apple()
是一个语句,为什么没有花括号和分号,是不是非有效的。你需要记住这是合法的,这是一个特殊的规定,不需要用括号环绕返回值为void的单行方法调用。public interface Predicate<T> {
boolean test(T t);
}
T -> boolean
,接收一个泛型T对象,返回一个boolean。// 判断空白字符串
Predicate<String> blankStrPredicate = s -> s != null && s.trim().length() == 0;
blankStrPredicate.test(" "); // true
// 判断苹果重量是否大于150
Predicate<Apple> heavyApplePredicate = a -> a.getWeight() > 150;
heavyApplePredicate.test(new Apple(100)); // false
注意,参数部分缺少了参数类型,是因为可根据上下文推断出Lambda表达式的参数类型,所以可以省略不写。比如这里因为将Lambda表达式赋值给一个Predicate 类型的变量,又因为函数描述符为 (T) -> boolean
,则可推断出参数T的实际类型为String。而且当只有一个参数时,可以将括号也省略。
public interface Consumer<T> {
void accept(T t);
}
T -> void
,接收一个泛型T对象,无返回值(void)。// 打印苹果重量
Consumer<Apple> appleWeighter =
a -> System.out.println("The apple weights " + a.getWeight() + " grams");
appleWeighter.accept(new Apple(200));
// The apple weights 200 grams
public interface Supplier<T> {
T get();
}
() -> T
,无入参,返回一个泛型T对象。Consumer<Apple> appleWeighter =
(a) -> System.out.println("The apple weights " + a.getWeight() + " grams");
// 生产200克的苹果
Supplier<Apple> heavyAppleSupplier = () -> new Apple(200);
appleWeighter.accept(heavyAppleSupplier.get());
public interface Function<T, R> {
R apply(T t);
}
T -> R
,接受一个泛型T对象,返回一个泛型R对象double unitPrice = 0.01;
// 计算苹果价格
Function<Apple, Double> priceAppleFunction = a -> a.getWeight() * unitPrice;
priceAppleFunction.apply(new Apple(100)); // 1
这里做个补充,上面这段代码特别的地方在于使用到了外部的局部变量。Lambda表达式使用外部变量有什么要求?对于Lambda表达式所在的主体(类)的实例变量和静态变量,可以无限制使用,但局部变量必须显示声明为final或实际上是final的。 声明为final好理解,什么是实际上是final的,意思就是不能被代码进行修改,比如这里的unitPrice虽然没有声明为final,但后续的代码并没有修改该变量,所以实际上也是final的。感兴趣的读者可以自己试下,对unitPrice进行修改,看下会发生什么。
public interface Comparator<T> {
int compare(T o1, T o2);
}
(T, T) -> int
,两个泛型T类型的入参,返回int类型Comparator<Apple> weightComparator =
(a1, a2) -> a1.getWeight() - a2.getWeight();
weightComparator.compare(new Apple(100), new Apple(150)); // -1
Function<Apple, Integer> weightor = a -> a.getWeight();
Function<Apple, Integer> weightor = Apple::getWeight;
目标引用::方法名称
。Function<String, Integer> fun = s -> Integer.parseInt(s);
Integer
的静态方法 parseInt
,可写为:Function<String, Integer> fun = Integer::parseInt;
Function<String, Integer> fun = s -> s.length();
String
类型实例 s
的 length
方法,可写为:Function<String, Integer> fun = String::length;
String s = "草捏子";
Supplier<Integer> len = () -> s.length();
s
的 length
方法,可写为:String s = "草捏子";
Supplier<Integer> len = s::length;
Comparator
对苹果进行排序,按重量从小到大:List<Apple> apples = Arrays.asList(new Apple("red", 50),
new Apple("red", 100),
new Apple("green", 100));
apples.sort(Comparator.comparing(Apple::getWeight));
Comparator
的静态方法comparing
简单介绍下,接受一个 Function
类型的参数,返回一个 Comparator
类型的实例,定义如下:public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
compareTo
,实现了重量从小到大的排序,那想按重量从大到小排序,怎么办呢?可以使用 Comparator
的 reversed
方法:apples.sort(Comparator.comparing(Apple::getWeight).reversed());
reversed
的实现如下:default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
Comparator
。你可能会好奇Comparator
作为一个接口,reversed
方法可以有具体的实现,接口的实例方法应该都是抽象方法,那它还是一个有效的函数式接口吗,或者说还是一个有效的接口吗?Comparator
的抽象方法只有一个 compare
,其他是具体方法,所以是合法的函数式接口。那么接口中为什么能定义具体方法呢?Java8 之前是不支持的,但在 Java8 中引入了 default
关键字。default
声明一个方法时,允许它是一个具体方法。这样的好处在于,我们可以在Lambda表达式之后直接跟上一个具体方法,对Lambda表达式增强,实现更复杂的功能。在后文介绍的用于复合表达式的方法都是接口中的 default
方法。apples.sort(Comparator.comparing(Apple::getWeight).reversed());
apples.sort(Comparator.comparing(Apple::getColor));
Comparator
。而使用 Comparator
的 thenComparing
方法可以继续连接一个 Comparator
,从而构建更复杂的排序:apples.sort(Comparator.comparing(Apple::getWeight)
.reversed().thenComparing(Apple::getColor));
Predicate
的 test
方法 (T) -> boolean
返回一个布尔表达式。类似 Java 在为布尔表达式提供的与或非,Predicate
中也有对应的方法 and
、or
、negate
。例如:// 重的苹果
Predicate<Apple> heavyApple = a -> a.getWeight() > 100;
// 红的苹果
Predicate<Apple> redApple = a -> a.getColor().equals("red");
// 轻的苹果
Predicate<Apple> lightApple = heavyApple.negate();
// 不红的苹果
Predicate<Apple> nonRedApple = redApple.negate();
// 重且红的苹果
Predicate<Apple> heavyAndRedApple = heavyApple.and(redApple);
// 重或红的苹果
Predicate<Apple> heavyOrRedApple = heavyApple.or(redApple);
Function
(T) -> R
,对输入做映射。我们通过将多个Function
进行组合,实现将一个Function
的输出作为另一个Function
的输入,是不是有管道的感觉。下面请看具体的方法。andThen
方法,a.andThen(b)
,将先执行a,再执行b。Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1); // 4
compose
方法,a.compose(b)
,将先执行b,再执行a。Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1); // 3
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️