首页 > 编程语言 >Java官方笔记9Lambda表达式

Java官方笔记9Lambda表达式

时间:2023-06-17 14:23:39浏览次数:52  
标签:Predicate 9Lambda Java strings length interface method 表达式 Lambda

Lambda Expression

有了Lambda Expression,就不用再写anonymous classes。

写Lambda,首先要找到它的类型。

There is a restriction on the type of a lambda expression: it has to be a functional interface.

函数接口,只有1个抽象方法的接口:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

默认方法do not count,所以下面这个也是函数接口:

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        // the body of this method has been removed
    }
}

静态方法也do not count,所以下面这个还是函数接口:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        // the body of this method has been removed
    }

    default Predicate<T> negate() {
        // the body of this method has been removed
    }

    default Predicate<T> or(Predicate<? super T> other) {
        // the body of this method has been removed
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        // the body of this method has been removed
    }

    static <T> Predicate<T> not(Predicate<? super T> target) {
        // the body of this method has been removed
    }
}

接着,实现函数接口的抽象方法,比如Predicate的抽象方法:

boolean test(T t);

实现Predicate<String>

Predicate<String> predicate =
    (String s) -> {
        return s.length() == 3;
    };

省略了返回类型和方法名,只保留参数列表和方法体,用->连接。

再简化:

Predicate<String> predicate = s -> s.length() == 3;

这就是最常见的Lambda,因为前面有String,编译器能够知道,所以这里把(String s)简写为s,然后只有一行,再把大括号和return省略掉。

无return,跟省略掉return:

Consumer<String> print = s -> System.out.println(s);

无参数,用()代替:

Runnable runnable = () -> System.out.println("I am running");

Lambda的使用:

List<String> retainStringsOfLength3(List<String> strings) {

    Predicate<String> predicate = s -> s.length() == 3;
    List<String> stringsOfLength3 = new ArrayList<>();
    for (String s: strings) {
        if (predicate.test(s)) {
            stringsOfLength3.add(s);
        }
    }
    return stringsOfLength3;
}

因为Lambda实现了接口的抽象方法,所以predicate.test(s)调用的实际上是Lambda。

Lambda不能修改变量值:

int calculateTotalPrice(List<Product> products) {

    int totalPrice = 0;
    Consumer<Product> consumer =
        product -> totalPrice += product.getPrice();  // 编译错误
    for (Product product: products) {
        consumer.accept(product);
    }
}

编译错误:Variable used in lambda expression should be final or effectively final

This process of accessing variable is called capturing: lambdas cannot capture variables, they can only capture values. A final variable is in fact a value.

也就是Lambda只能读,不能写。

使用Lambda

Lambda是JDK8跨时代的语法技术,它引领了大量的JDK API重写。

java.util.function中包含了很多可供实现的函数接口,functional interfaces:

https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/function/package-summary.html

比如:

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

Lambda实现:

Supplier<String> supplier = () -> "Hello Duke!";`

使用:

Random random = new Random(314L);
Supplier<Integer> newRandom = () -> random.nextInt(10);

for (int index = 0; index < 5; index++) {
    System.out.println(newRandom.get() + " ");
}

比如:

List<String> strings = ...; // really any list of any kind of objects
Consumer<String> printer = s -> System.out.println(s);
strings.forEach(printer);

比如:

List<String> immutableStrings =
        List.of("one", "two", "three", "four", "five");
List<String> strings = new ArrayList<>(immutableStrings);
Predicate<String> isOddLength = s -> s.length() % 2 == 0;
strings.removeIf(isOddLength);
System.out.println("strings = " + strings);

比如:

@FunctionalInterface
public interface Function<T, R> {

    R apply(U u);

    // default and static methods removed
}
Function<String, Integer> toLength = s -> s.length();
String word = ...; // any kind of word will do
int length = toLength.apply(word);

以上是JDK中最经典的4种function interface

  • Supplier<T>
  • Consumer<T>
  • Predicate<T>
  • Function<T, R>

总结一下,Lambda的使用就是先实现function interface的abstract method,然后①接口实例调用abstract method,或者②接口实例被forEach等调用。

Lambda使得function interface能够实例化:Predicate<String> isOddLength = s -> s.length() % 2 == 0;

The java.util.function package is now central in Java, because all the lambda expressions you are going to use in the Collections Framework or the Stream API implement one of the interfaces from that package.

发现没有,把:

List<String> strings = ...; // really any list of any kind of objects
Consumer<String> printer = s -> System.out.println(s);
strings.forEach(printer);

简写一下,去掉中间的接口实例:

List<String> strings = ...; // really any list of any kind of objects
strings.forEach(s -> System.out.println(s));

这不就是常见到的Lambda的形态嘛。换句话说,Lambda的结果就是函数接口实例(Function interface instance),使用Lambda本质上就是调用函数,Java中没有函数的概念,通过function interface的abstract method,引入了函数,从而造就了Lambda。

Method References

Sometimes people call these lambda expressions "anonymous methods", since it is just that: a method that has no name, and that you can move around your application, store in a field or a variable, pass as an argument to a method or a constructor and return from a method.

Consumer<String> printer = s -> System.out.println(s);

这里的Lambda没有什么逻辑,其实就是System.out.println()方法的引用,所以可以简写为Bound(System.out优化前后一致):

Consumer<String> printer = System.out::println;

Static:

IntBinaryOperator max = (a, b) -> Integer.max(a, b);
IntBinaryOperator max = Integer::max;

Unbound(优化前s,优化后String;优化前user,优化后User):

Function<String, Integer> toLength = s -> s.length();
Function<String, Integer> toLength = String::length;
Function<User, String> getName = user -> user.getName();
Function<String, Integer> toLength = User::getName;
BiFunction<String, String, Integer> indexOf = (sentence, word) -> sentence.indexOf(word);
BiFunction<String, String, Integer> indexOf = String::indexOf;

Constructor:

Supplier<List<String>> newListOfStrings = () -> new ArrayList<>();
Supplier<List<String>> newListOfStrings = ArrayList::new;

总结:

Combining Lambda Expressions

Function interface中的defualt method该登场了。

Predicate<String> nonNull = s -> s != null;
Predicate<String> nonEmpty = s -> s.isEmpty();
Predicate<String> shorterThan5 = s -> s.length() < 5;

Predicate<String> p = nonNull.and(nonEmpty).and(shorterThan5);

这里的and()就是default method,它实现了Lambda的组合,也就是链式调用的既视感。

稍复杂点的:

Predicate<String> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNullOrEmpty = isNull.or(isEmpty);
Predicate<String> isNotNullNorEmpty = isNullOrEmpty.negate();
Predicate<String> shorterThan5 = s -> s.length() < 5;

Predicate<String> p = isNotNullNorEmpty.and(shorterThan5);

Comparators

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2);
}

Lambda实现:

Comparator<Integer> comparator = (i1, i2) -> Integer.compare(i1, i2);
Comparator<Integer> comparator = Integer::compare;

Comparator的静态方法:

Comparator<String> comparator = Comparator.comparing(String::length);

This comparing() method is a static method of the Comparator interface. It takes a Function as an argument, that should return a type that is an extension of Comparable.

使用:

List<User> users = ...; // this is your list
Comparator<User> byName = Comparator.comparing(User::getName);
users.sort(byName);

还有默认方法thenComparing:

Comparator<User> byFirstNameThenLastName =
        Comparator.comparing(User::getFirstName)
                  .thenComparing(User::getLastName);

参考资料:

Lambda Expressions https://dev.java/learn/lambdas/

标签:Predicate,9Lambda,Java,strings,length,interface,method,表达式,Lambda
From: https://www.cnblogs.com/df888/p/17487426.html

相关文章

  • JavaWeb
    review:1.post提交方式下的设置编码,防止中文乱码request.setCharacterEncoding("utf-8");get提交方式,tomcat8开始,编码不需要设置tomcat8之前,get方式设置比较麻烦:Stringfname=request.getParameter("fname");byte[]bytes=fname.getBytes("iso-8859-1");fn......
  • Java代理模式
    一、基本概念1、代理模式:使用代理对象来代替目标对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。2、有静态代理和动态代理两种实现方式 二、静态代理静态代理是在编译时期就已经确定代理类的代码,在程序运行前就已经存在。静态代理......
  • Java序列化
    一、序列化和反序列化序列化:将数据结构或对象转换成二进制字节流的过程反序列化:将在序列化过程中产生的二进制字节流转换成数据结构或对象的过程 常见场景:1、网络传输时,对象需要先被序列化,接收到后再进行反序列化2、将对象持久化到磁盘、文件时需要先进行序列化,从磁盘或者......
  • Java线程池与异常处理
    线程池线程池的创建代码ThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,......
  • Java值传递
    一、形参&实参实参:实际传递给方法的参数形参:用于定义方法,接收实参,不需要有确定的值 二、值传递&引用传递值传递:方法接受的是实参值的拷贝,会创建副本引用传递:方法接收的是实参所引用对象在堆中的地址,不会创建副本,对形参的修改将影响到形参Java只有值传递  publicstati......
  • JAVA集合
    一、集合框架概览Java集合也叫做容器,由两大接口派生而来,一个是collection接口,主要用于存放单一元素,另一个是map接口,用于存放键值对。collection有三个子接口:list、set、queue。相较于数组,Java集合的优势在于它们的大小可变、支持泛型、具有内建算法,比如add(),remove()等。 l......
  • java——微服务——spring cloud——Nacos——Nacos配置共享
       ......
  • java——微服务——spring cloud——Nacos——Nacos配置热更新
                                                          ......
  • Java 命名规范
    包命名规范包(Package)的作用是将功能相似或相关的类或者接口进行分组管理,便于类的定位和查找,同时也可以使用包来避免类名的冲突和访问控制,使代码更容易维护。通常,包名使用小写英文字母进行命名,并使用“.”进行分割,每个被分割的单元只能包含一个名词。一般地,包命名常采用顶级域......
  • 【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实
    异常传播有时候,您可能需要重新抛出捕获到的异常。这种情况通常发生在捕获到Error或RuntimeException时,因为您可能没有预料到这些异常,但在声明捕获Throwable和Exception时,它们也被包含在内了。为了解决这个问题,Guava提供了多种方法来判断异常类型并重新抛出异常。例如:try{......