Java基础之lambda表达式(JDK1.8新特性)

简介: Java基础之lambda表达式(JDK1.8新特性)

Lambda表达式

Lambda表达式允许把函数作为一个方法的参数(函数作为参数传递进方法中)。函数式接口有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为lambda表达式。

各种函数式接口

java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter

JDK1.8 新增加的函数接口:

java.util.function

java.util.function 包下包含了很多类,用来支持Java的函数式编程

接口 &描述
BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果
BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
BinaryOperaror 代表了一个作用于两个同类型操作符的操作,并且返回了操作符同类型的结果
BiPredicate<T,U> 代表了一个两个参数的boolean值方法
BooleanSupplier 代表了boolean值结果的提供方
Consumer 代表了接受一个输入参数并且无返回的操作
DoubleBinaryOperator 代表了作用于两个double值操作符的操作,并且返回了一个double值的结果
DoubleConsumer 代表了一个接受double值参数的操作,并且不返回结果
DoubleFunction 代表接受一个double值参数的方法,并且有返回值
Comparator 这个接口最主要的作用就是比较,其核心的方法是 compare(T o1, T o2),当 o1比o2小返回-1,当o1等于o2返回0,当o1大于o2返回1

Lambda的语法

(parameters) -> expression
(parameters) ->{ statements; }
  1. 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
  2. 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
  3. 可选的大括号:如果主体包括了一个语句,就不需要使用大括号
  4. 可选的返回关键字:如果主体只有一个表达式返回值,则编译器会自动返回值,大括号需要指明表达式返回的一个数值。

Lambda 表达实例

//1. 不需要参数,返回值为5
        ()->5;
        //2. 接收一个参数(数字类型),返回其2倍的值
        x ->2*x
        //3.接受2个参数(数字),并返回他们的差值
        (x,y)->x - y;
        //4.接收2个int型整数,返回他们的和
        (int x,int y)->x+y;
        //5. 接收一个String对象,并在控制台打印,不返回任何值
        (String s)->System.out.print(s);

举例说明

public class Java8Tester {
    public static void main(String[] args) {
   Java8Tester tester = new Java8Tester();
        //类型声明
        MathOperation addition = (int a, int b) -> a + b;
        //不用声明类型
        MathOperation subtraction = (a, b) -> a - b;
        //大括号中的返回语句
        MathOperation multipliaction = (int a, int b) -> {
            return a * b;
        };
        //没有大括号及返回语句
        MathOperation division = (int a, int b) -> a / b;
        System.out.println("10+5=" + tester.operate(10, 5, addition));
        System.out.println("10-5=" + tester.operate(10, 5, subtraction));
        System.out.println("10*5=" + tester.operate(10, 5, multipliaction));
        System.out.println("10/5=" + tester.operate(10, 5, division));
        //不用括号
        GreetingService greetingService1 = message -> System.out.println("Hello " + message);
        //用括号
        GreetingService greetingService2 = (message) -> System.out.println("Hello " + message);
        greetingService1.sayMessage("Runoob");
        greetingService2.sayMessage("Google");
   }
    interface MathOperation{
        int operation(int a, int b);
    }
    interface GreetingService{
        void sayMessage(String message);
    }
    private int operate(int a, int b, MathOperation mathOperation) {
        return mathOperation.operation(a, b);
    }
}

变量作用域

lambda 表达式只能引用标记了final的外层局部变量,也就是说不能再lambda内部修改定义在域外的局部变量,否则会编译报错。 在lambda表达式中,只能引用值不会改变的变量。 这是因为如果在lambda表达式中改变变量,并发执行多个动作时就会不安全。对于目前为止我们看到的动作不会发生这种情况。

public class Java8Test2 {
    final static String salutation = "Hello!";
    public static void main(String[] args) {
        GreetingService greetingService = message -> System.out.println(salutation + message);
        greetingService.sayMessage("Runoob");
    }
    interface GreetingService {
        void sayMessage(String message);
    }
}

处理lambda 表达式

使用lambda表达式的重点是延迟执行。毕竟,如果想要立即执行代码,完全可以直接执行,而无需把它包装在一个

lambda表达式中。之所以希望以后执行代码,这有很多原因,如:

  • 在一个单独的线程中运行代码:
  • 多次运行代码;
  • 在算法的适当位置运行代码(例如:排序中的比较操作);
  • 发生某种情况时执行代码(如,点击了一个按钮,数据到达,等等);
  • 只在必要时才运行代码。

例如:假设你想要执行一个动作n次,将这个动作和重复次数传递到一个 repeat 方法:

repeat(10,()->System.out.println("Hello,world"))

要接受这个lambda表达式,需要选择一个函数式接口。例如,我们可以使用 Runnable 接口:

public static void repeat(int n,Runnable action){
      for(int i=0;i<n;i++){
        action.run();
      }
 }

如果要告诉动作出现在某次迭代中。

/**
     * 在某次迭代中执行动作
     * @param n
     * @param action
     */
    public static void repeat(int n, IntConsumer action) {
        for (int i = 0; i < n; i++) {
            action.accept(i);
        }
    }

变量作用域

lambda 表达式有3个部分

  1. 一个代码块;
  2. 参数;
  3. 自由变量的值,这里指非参数而且不在代码中定义的变量。

函数式接口

函数接口,是指内部只有一个抽象方法的接口。但是可以有多个非抽象方法的接口,函数式接口可以被隐式转换为lambda表达式。

使用实例1
public class PredicateTest {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        /**
         *
         * Predicate<Integer> dispatcher = n -> true;
         n 是一个参数传递到Predicate接口的test方法
         n 如果存在test方法返回true
         */
        System.out.println("输出所有数据:");
        //传递参数n
        eval(list, n -> true);
//        Predicate<Integer> dispatcher = n -> true;
//        n 是一个参数传递到Predicate接口的test方法
//        如果n%2为0,test方法返回true
        System.out.println("******输出所有的偶数:");
        eval(list, n -> n % 2 == 0);
//        Predicate<Integer> predicate2=n->n>3
//        n是一个参数传递到Predicate接口的test方法
//        如果n大于3 test方法返回true
        System.out.println("输出大于3的所有数字:");
        eval(list, n -> n > 3);
    }
    public static void eval(List<Integer> list, Predicate<Integer> predicate) {
        for (Integer n : list) {
            if (predicate.test(n)) {
                System.out.println(n+" ");
            }
        }
    }
}
使用实例2
public class CompareTest {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "pear");
        compareLength("apple", "banana", (n1, n2) -> -1);
        compareLength("banana", "pear", (n1, n2) -> 1);
        compareLength("banana", "banana", (n1, n2) -> 0);
        words.sort(Comparator.comparingInt(String::length));
    }
    public static void compareLength(String n1, String n2, Comparator comparator) {
        if (comparator.compare(n1, n2) == 0) {
            System.out.println("n1==n2" + n1 + "  " + n2);
        }
        if (comparator.compare(n1, n2) > 0) {
            System.out.println("n1>n2" + n1 + "  " + n2);
        }
        if (comparator.compare(n1, n2) < 0) {
            System.out.println("n1<n2" + n1 + "  " + n2);
        }
    }
}
使用示例3(集合排序)

现在我们有一个集合list,集合的里的数据类型是HashMap。那么我们如何根据HashMap中的某个key给list排序呢?

public class ListSortTest {
    public static void main(String[] args) {
        List<Map<String, Object>> list = new ArrayList<>();
        Map<String, Object> map1 = new HashMap<>();
        map1.put("id", 2);
        map1.put("name", "张二");
        list.add(map1);
        Map<String, Object> map2 = new HashMap<>();
        map2.put("id", 1);
        map2.put("name", "张一");
        list.add(map2);
        Map<String, Object> map3 = new HashMap<>();
        map3.put("id", 3);
        map3.put("name", "张三");
        list.add(map3);
        //升序排列
        Collections.sort(list, (o1, o2) -> {
            int o1Id = (int) o1.get("id");
            int o2Id = (int) o2.get("id");
            if (o1Id > o2Id) {
                return 1;
            } else {
                return -1;
            }
        });
        //降序排列
        Collections.sort(list, new Comparator<Map<String, Object>>() {
            @Override
            public int compare(Map<String, Object> o1, Map<String, Object> o2) {
                int o1Id = (int) o1.get("id");
                int o2Id = (int) o2.get("id");
                if (o1Id > o2Id) {
                    return -1;
                } else {
                    return 1;
                }
            }
        });
    }
}
使用示例4(按照对象属性给list排序)
public class ListSortTest2 {
    public static void main(String[] args) {
        Student student1 = new Student(1, "张三");
        Student student2 = new Student(2, "李四");
        List<Student> studentList = new ArrayList<>();
        studentList.add(student1);
        studentList.add(student2);
        //升序
        studentList.sort((o1, o2) -> {
            int o1Id = o1.getId();
            int o2Id = o2.getId();
            if (o1Id > o2Id) {
                return 1;
            } else {
                return -1;
            }
        });
        //降序
        studentList.sort((o1, o2) -> {
            int o1Id = o1.getId();
            int o2Id = o2.getId();
            if (o1Id > o2Id) {
                return -1;
            } else {
                return 1;
            }
        });
    }

使用示例4

自定义一个函数式接口

public class SimpleLambda {
    public static void main(String[] args) {
        start(()-> System.out.println("调用函数式接口"));
    }
    public static void start(MyRunnable runnable) {
        new Thread(runnable).start();
    }
    //1. 只能有一个抽象方法
    //2.默认方法除外
    //3.可以加FunctionalInterface注解,也可以不加
    public interface MyRunnable extends Runnable {
        default void myRun() {
        }
    }
}
  1. 自定义一个函数式接口,传入参数,多种不同的代码编写特性:
public class SimpleLambda2 {
    public static void main(String[] args) {
        // 1.单行表达式,可以省略return
        run(name -> String.format(name));
        // 2.代码块
        run(name -> {
            String name1 = name;
            return "序列化" + name1;
        });
        //3.方法引用,静态方法引用
        run(SimpleLambda2::toFormat);
        //4.普通方法引用
        run(new SimpleLambda2()::toFormat2);
    }
    static void run(Format format) {
        format.format("飞哥");
    }
    static String toFormat(String param) {
        return "序列化" + param;
    }
    String toFormat2(String param) {
        return "序列化" + param;
    }
    public interface Format {
        String format(String name);
    }
}
  1. 自定义一个函数式接口,带泛型的方法型函数
public class SimpleLambda4 {
    public static void main(String[] args) {
        Apple apple = new Apple(1, "红色", 12, "安徽");
        Banner banner1 = doBuild(apple, apple1 -> {
            Banner banner = new Banner();
            banner.setPrice(apple1.getPrice());
            banner.setColor(apple1.getColor());
            return banner;
        });
        System.out.println(banner1);
    }
    public static Banner doBuild(Apple apple, Format format) {
        return format.build(apple);
    }
    @FunctionalInterface
    public interface Format<T extends Apple, R extends Banner> {
        R build(T t);
    }
}

总结

本文详细介绍了lambda表达式,lambda表达式是JDK1.8最重要的特性。基本上所有的内部类都可以用lambda表达式来表示。灵活的运用lambda表达式和函数式接口可以大大的简化的程序开发。

参考

Java 8 Lambda 表达式

[Java 8 函数式接口](

相关文章
|
1月前
|
数据采集 监控 Oracle
GraalVM 24 正式发布阿里巴巴贡献重要特性 —— 支持 Java Agent 插桩
阿里巴巴是 GraalVM 全球顾问委员会的唯一中国代表,阿里云程序语言与编译器团队和可观测团队合作实现了 GraalVM 应用的无侵入可观测能力,并在 ARMS 平台上线了该功能。目前在 GraalVM 24 中发布的是支持 Java agent 的第一步,其余能力将在 GraalVM 的后续版本中陆续发布。
173 21
|
1天前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
13 0
|
2月前
|
Java 测试技术 调度
JDK21有没有什么稳定、简单又强势的特性?
这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。
JDK21有没有什么稳定、简单又强势的特性?
|
1月前
|
Java 编译器 API
Java Lambda 表达式:以 Foo 接口为例深入解析
本文深入解析了 Java 8 中 Lambda 表达式的用法及其背后的函数式接口原理,以 `Foo` 接口为例,展示了如何通过简洁的 Lambda 表达式替代传统匿名类实现。文章从 Lambda 基本语法、函数式接口定义到实际应用层层递进,并探讨默认方法与静态方法的扩展性,最后总结常见误区与关键点,助你高效优化代码!
55 0
|
2月前
|
SQL Rust Java
怎么理解Java中的lambda表达式
Lambda表达式是JDK8引入的新语法,用于简化匿名内部类的代码写法。其格式为`(参数列表) -&gt; { 方法体 }`,适用于函数式接口(仅含一个抽象方法的接口)。通过Lambda表达式,代码更简洁灵活,提升Java的表达能力。
|
2月前
|
缓存 运维 Java
Java静态代码块深度剖析:机制、特性与最佳实践
在Java中,静态代码块(或称静态初始化块)是指类中定义的一个或多个`static { ... }`结构。其主要功能在于初始化类级别的数据,例如静态变量的初始化或执行仅需运行一次的初始化逻辑。
96 4
|
5月前
|
容器
jdk8新特性-详情查看文档
jdk8新特性-详情查看文档
100 7
|
5月前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
12天前
|
Java 关系型数据库 MySQL
在Linux平台上进行JDK、Tomcat、MySQL的安装并部署后端项目
现在,你可以通过访问http://Your_IP:Tomcat_Port/Your_Project访问你的项目了。如果一切顺利,你将看到那绚烂的胜利之光照耀在你的项目之上!
82 41
|
4天前
|
开发框架 Java 关系型数据库
在Linux系统中安装JDK、Tomcat、MySQL以及部署J2EE后端接口
校验时,浏览器输入:http://[your_server_IP]:8080/myapp。如果你看到你的应用的欢迎页面,恭喜你,一切都已就绪。
104 17

热门文章

最新文章