还记得小刘刚进公司的时候,那个叫苦恼,公司代码恰巧全部用Java8 首先我们要知道如何写Lambda表达式,或者说怎么样才能写出有效的Lambda表达式,这就需要了解其语法。 Lambda表达式由三部分组成: 有两种风格,分别是: 表达式-风格 块-风格 依据上面的风格介绍,来试着判断下面给出的示例是否有效: 解析:(1)是块风格,没有语句; (2)是表达式风格,一个字符串表达式;(3)是块风格,有花括号和返回语句;(4)非有效,写了返回语句,但缺少花括号,补上花括号和分号,为块风格,而去掉return则为表达式风格;(5)非有效,”Apple”是一个字符串表达式,不是一个语句,加上return,或者去掉分号和花括号。 2. 函数式接口 Lambda 表达式写好了,我们要知道哪里能用 可见上面三个接口都只有一个抽象方法,但是三个方法的签名都不一样,这要求Lambda表达式与实现接口的方法签名要一致。下面用函数描述符来表示上述三个方法的签名,箭头前面是方法的入参类型,后面是返回类型。 看call方法的示例,你是否会疑惑,new Apple()是一个语句,为什么没有花括号和分号,是不是非有效的。你需要记住这是合法的,这是一个特殊的规定,不需要用括号环绕返回值为void的单行方法调用。 3. 常用的函数式接口 适用场景:表示一个涉及类型T的布尔表达式。 // 判断空白字符串 // 判断苹果重量是否大于150 注意,参数部分缺少了参数类型,是因为可根据上下文推断出Lambda表达式的参数类型,所以可以省略不写。比如这里因为将Lambda表达式赋值给一个Predicate类型的变量,又因为函数描述符为(T) -> boolean,则可推断出参数T的实际类型为String。而且当只有一个参数时,可以将括号也省略。这是Java新出的语法格式,没必要特地的加上 类型推断 3.2 Consumer // 打印苹果重量 3.3 Supplier 3.4 Function 这里做个补充,上面这段代码特别的地方在于使用到了外部的局部变量。Lambda表达式使用外部变量有什么要求?对于Lambda表达式所在的主体(类)的实例变量和静态变量,可以无限制使用,但局部变量必须显示声明为final或实际上是final的。 声明为final好理解,什么是实际上是final的,意思就是不能被代码进行修改,比如这里的unitPrice虽然没有声明为final,但后续的代码并没有修改该变量,所以实际上也是final的。感兴趣的读者可以自己试下,对unitPrice进行修改,看下会发生什么。 3.5 Comparator 4. 方法引用 Java还提供了一种更简洁的写法,先上示例: 可改写为: 这种写法被称作方法引用,仅当在 (1)指向静态方法的方法引用 调用了 Integer 的静态方法 parseInt,可写为: (2)指向任意类型实例方法的方法引用 调用了 String 类型实例 s 的 length 方法,可写为: 目标引用写实例的类型。和第一种写法相同,只不过第一种的方法是静态方法,这里是实例方法。 调用了局部变量 s 的 length 方法,可写为: 目标引用写变量名,区别于前两种。 我们使用 Comparator 对苹果进行排序,按重量从小到大: 对 Comparator 的静态方法comparing 简单介绍下,接受一个 Function 类型的参数,返回一个 Comparator 类型的实例,定义如下: 通过使用compareTo,实现了重量从小到大的排序,那想按重量从大到小排序,怎么办呢?可以使用 Comparator 的 reversed 方法: reversed 的实现如下: 使用工具类得到一个反序的 Comparator。你可能会好奇Comparator 作为一个接口,reversed 方法可以有具体的实现,接口的实例方法应该都是抽象方法,那它还是一个有效的函数式接口吗,或者说还是一个有效的接口吗? 回想下第二节的内容,函数式接口是只定义一个抽象方法的接口。Comparator的抽象方法只有一个 compare,其他是具体方法,所以是合法的函数式接口。那么接口中为什么能定义具体方法呢?Java8 之前是不支持的,但在 Java8 中引入了 default 关键字。 先后用两个Comparator。而使用 Comparator 的 thenComparing 方法可以继续连接一个 Comparator,从而构建更复杂的排序: 5.2 谓词复合(Predicate) 5.3 函数复合(Function)
希腊字母表中排序第十一位的字母,大写为Λ,英语名称为Lambda。 (图片引自百科)Lambad
写的 ,看得我一脸懵逼,这什么代码是 人写的吗? 一堆箭头符号,飞来飞去 –> () 可以想象一下,那时候的绝望,还在经过几年的磨练,已经算是,熟练了,今天就来写篇文章,讲讲自己的理解,大神勿喷
参数列表
箭头
主体
(parameters) -> expression
(parameters) -> { statements; }
() -> {}
() -> "Apple"
() -> { return "Apple"; }
(Integer i) -> return "Apple" + i
(String s) -> { "Apple"; }
Lambda
表达式。已知Lambda表达式可看作是匿名内部类的实现,那对于匿名内部类来说最重要的是类所实现的接口,而Lambda表达式是否可用于所有接口?答案“不是的”,Lambda表达式对接口有一定的要求,必须是函数式接口。
所谓的函数式接口指的是只定义一个抽象方法的接口。
例如:public interface Comparator<T> { int compare(T o1, T o2); } public interface Runnable { void run(); } public interface Callable<V> { V call() throws Exception; }
compare:(T, T) -> int,两个泛型T类型的入参,返回int类型 Lambda表达式:(Apple a1, Apple a2) -> a1.getWeight - a2.getWeight run:() -> void,无入参,无返回值 Lambda表达式:() -> { System.out.println("Hi"); } call:() -> V,无入参,返回一个泛型V类型的对象 Lambda表达式:() -> new Apple()
下面介绍在Java中内置的常用Lambda表达式:3.1 Predicate public interface Predicate<T> { boolean test(T t); }
test:T -> boolean
,接收一个泛型T对象,返回一个boolean。Predicate<String> blankStrPredicate = s -> s != null && s.trim().length() == 0; blankStrPredicate.test(" "); // true
Predicate<Apple> heavyApplePredicate = a -> a.getWeight() > 150; heavyApplePredicate.test(new Apple(100)); // false
public interface Consumer<T> { void accept(T t); }
accept
:T -> void,接收一个泛型T对象,无返回值(void)。
适用场景:访问类型T的对象,对其执行某些操作。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(); }
get:() -> T
,无入参,返回一个泛型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); }
apply:T -> R
,接受一个泛型T对象,返回一个泛型R对象
适用场景:将输入对象转换输出。double unitPrice = 0.01; // 计算苹果价格 Function<Apple, Double> priceAppleFunction = a -> a.getWeight() * unitPrice; priceAppleFunction.apply(new Apple(100)); // 1
public interface Comparator<T> { int compare(T o1, T o2); }
compare:(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<入参类型T,返回值R>
变量 = 和R 返回值一致即可Function<Apple, Integer> weightor = Apple::getWeight;
Lambda
表达式中直接调用了一个方法时可以使用。其写法为目标
引用::方法名称。
根据目标引用可分为三类:
目标引用为类,调用其静态方法,例如:Function<String, Integer> fun = s -> Integer.parseInt(s);
Function<String, Integer> fun = Integer::parseInt;
目标引用为实例对象,调用其实例方法,例如:Function<String, Integer> fun = s -> s.length();
Function<String, Integer> fun = String::length;
(3)指向现存外部对象实例方法的方法引用
目标引用为现存外部对象,调用其实例方法,例如:String s = "全栈小刘"; Supplier<Integer> len = () -> s.length();
String s = "全栈小刘"; Supplier<Integer> len = s::length;
5. 复合Lambda表达式
之前的例子都是使用的单个Lambda表达式,现在我们把多个Lambda表达式组合在一起,构建更复杂一点的表达式。
5.1 比较器复合(Comparator)
List<Apple> apples = Arrays.asList(new Apple("red", 50), new Apple("red", 100), new Apple("green", 100)); apples.sort(Comparator.comparing(Apple::getWeight));
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)); }
apples.sort(Comparator.comparing(Apple::getWeight).reversed());
default Comparator<T> reversed() { return Collections.reverseOrder(this); }
当在接口中用default声明一个方法时,允许它是一个具体方法。这样的好处在于,我们可以在Lambda表达式之后直接跟上一个具体方法,对Lambda表达式增强,实现更复杂的功能。在后文介绍的用于复合表达式的方法都是接口中的 default 方法。
下面我们试着实现更复杂的排序,在按重量从大到小排序后,按颜色排序:apples.sort(Comparator.comparing(Apple::getWeight).reversed()); apples.sort(Comparator.comparing(Apple::getColor));
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
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算