java8新特性

您所在的位置:网站首页 定义一个员工类为父类 java8新特性

java8新特性

2023-03-13 20:01| 来源: 网络整理| 查看: 265

目录定义lambda表达式的语法需求函数式接口方法引用构造器引用总结定义jdk8发布新特性中,lambda是一大亮点之一。lambda表达式能够简化我们对数据的操作,减少代码量,大大提升我们的开发效率...

目录定义lambda表达式的语法需求函数式接口方法引用构造器引用总结

定义

jdk8发布新特性中,lambda是一大亮点之一。lambda表达式能够简化我们对数据的操作,减少代码量,大大提升我们的开发效率。

Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

Lambda表达式可以表示闭包。如果你之前了解Scala和js函数式编程,将会更加快速上手和学习Java8的lambda新特性。

lambda表达式的语法

Lambda表达式在Java8中引入了一个新的语法操作符号,即:->,它将Lambda表达式分为两部分。

左侧

Lambda表达式左侧为入参参数。

右侧

Lambada表示式的右侧表示执行的功能。

总结就是:

(parameters) -> expression 或 (parameters) ->{ statements; }

以下是Lambda表达式几种语法格式:

1.无参,无返回值,典型的一个例子是Runnable匿名内部类的使用。

// java 8之前的写法 Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " >---Lambda"); } }; // 使用lambda的写法 Runnable r = () -> System.out.println(Thread.currentThread().getName() + " >---Lambda");

2.一个参数的使用

// java 8 之前的写法 Consumer consumer = new Consumer() { @Override public void accept(String s) { System.out.println(s); } }; // Lambda表达式的写法 Consumer consumer = (par)->System.out.println(par); consumer.accept("xixi"); 一个参数的小括号可以省略,简化如下: Consumer consumer = par->System.out.println(par);

3.两个参数的使用

// java 8之前的写法 Comparator comparator = new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }; // 使用Lambda表达式的方法 ,当只有一条语句的时候,大括号和return都可以省略 Comparator comparator=(x,y) -> x.compareTo(y);

观察上面的代码后,我们发现使用Lambda表达式,在Lambda表达式中并没有指定入参的参数类型。这个编译和运行没有报错,这个是怎么判断出来的呢?

很简单是类型推断的作用,java8中有个很大的变化,就是类型推断,简单来说javac在编js译代码时候,会根据程序的上下文来推断出Lambda表达式参数类型。

例如上文中的下面这个代码:

Comparator comparator=(x,y) -> x.compareTo(y);

这里在编译的时候,在执行x.compareTo(y)的时候根据类型推断,因为这个接口定义数据的泛型是Intger,所以根据类型推断会自动调用Integer.compareTo方法。

为理解lambda表达式的作用,以及简化我们开发。这儿将会举个小小的例子。

需求

王小明所在的公司,每一个月都会进行薪资财务报表,王小明每一个月都会自己对员工们的薪资做统计,以了解公司财务支出和订单提成等需求。常常有做订单提成排名和总工资排名的这样的一个需求。

我们将定义以下类,来完成王小明的统计需求。

基本员工类

package com.codegeek.lambda; import lombok.*; @Setter @Getter @NoArgsConstructor @ToString public class Employee { /** * 员工姓名 */ private String name; /** * 员工年龄 */ private int age; /** * 基本薪水 */ private double basicSalary; /** * 订单成交总额 */ private double dealTotalPrice; public Employee(String name, int age, double basicSalary,double dealTotalPrice) { this.name = name; this.age = age; this.basicSalary = basicSalary; this.dealTotalPrice = dealTotalPrice; } /** * 员工总薪资 * * @return Double */ public Double getTotalSalary() { return this.basicSalary + this.dealTotalPrice * 0.04; } }

现在假设在A部门有,青龙,白虎,朱雀,玄武 四个部门人员。下面是他们上个月基本薪资的情况。

Employee qingLong = new Employee("青龙", 25, 5500, 7500); Employee baiHu = new Employee("白虎", 27, 5000, 9000); Employee zhuQue = new Employee("朱雀", 22, 3800, 4500); Employee xuanWu = new Employee("玄武", 24, 3300, 3300); List employees = Arrays.asList(qingLong, baiHu, zhuQue, xuanWu);

现在有个统计的需求是,按员工年龄从小到大排列,并获取员工姓名列表。让我们分别使用Lambda表达式和java8之前的做法。

java8之前通常的做法

// 员工列表先进行排序 employees.sort(new Comparator() { @Override public int compare(Employee o1, Employee o2) { Integer age1 = o1.getAge(); http://www.cppcns.com Integer age2 = o2.getAge(); return age1.compareTo(age2); } }); // 遍历排序后的列表并输出员工姓名 for (Employee employee : employees) { System.out.println(employee.getName()); }

使用Lambda的做法

employees.stream().sorted((o1, o2) -> o1.getAge().compareTo(o2.getAge())) .forEach(o -> System.out.println(o.getName()));

看到这里我们一定知道Lambda表达式使用的方便,确实减少了很多代码的使用。

函数式接口

只包含一个抽象方法的接口,称为函数式接口。

使用Lambda表达式创建该对象接口的对象,如果Lambda抛出一个受检异常,通常需要在目标接口使用@FunctionalInterface注解,来声明标记了该注解的接口是一个函数式接口。

例如:

Consumer consumer = par->System.out.println(par);

就是一个典型的消费型函数式接口。

java8新特性-lambda表达式入门学习心得

注意观察该接口的源代码,只包含一个抽象的方法的接口是函数式接口,下面andThen是一个默认方法,并不属于抽象方法。不要被迷惑了。

内建函数式的接口

jdk8中默认定义了很多函数式接口,主要使用的有下面四个。

函数式接口参数类型返回类型使用说明Consumer 消费型接口Tvoid对类型T的数据进行操作,抽象方法 void accept(T t)Supplier 供给型接口无T返回类型T的对象,抽象方法 T get();Function 函数型接口TR对类型T对象进行操作,返回类型R的对象,抽象方法R apply(T t)Predicate 断言型接口Tbolean对类型T对象进行操作,返回类型boolean,抽象方法boolean test(T t)

四大函数式接口的使用

public class FourFunctionsTest { // 消费式接口 @Test public void testConsumer() { Consumer consumer = x -> System.out.println(x); consumer.accept(1); } // 供给式接口 @Test public void testSupplier() { Supplier supplier = () -> { StringBuffer sb = new StringBuffer(); return sb.append("我").append(520).append("you").toString(); }; System.out.println(supplier.get()); } // 断言式接口 @Test public void testPredicate() { Predicate predicate = x -> x == 1L; System.out.println(predicate.test(2L)); } // 函数式接口 @Test public void testFunction() { Function function = x -> x > 3; System.out.println(function.apply(4)); } }

自定义函数式接口

上面我们举例A部门的四个员工,找出工资大于5000的员工。

// 使用策略式接口 @FunctionalInterface // 函数式接口(检查)只能有一个抽象方法 public interface MyFilter { /** * 获取指定想要的employee对象 * * @param t * @return */ boolean getWant(T t); } /** * 策略设计模式 */ public List needEmployee(List employeeList, MyFilter filter) { List employees = new ArrayList(); for (Employee employee : employeeList) { if (filter.getWant(employee)) { employees.add(employee); } } return employees; } // 匿名内部类 List employees1 = needEmployee(employees, new MyFilter() { @Override public boolean getWant(Employee employee) { return employee.getTotalSalary() >= 5000; } }); // 使用策略者设计模式Lambda简化 needEmployee(employees, employee -> mployee.getTotalSalary() >= 5000);

看了上面代码,如果还想简化怎么做呢?这里可以使用java 8的Stream API可以大大简化以上繁多的代码。

employees.stream().filter(e -> e.getTotalSalary() > 5000d).map(Employee::getName).forEach(System.out::println);

看到这儿,可能刚刚入门的同学会懵逼,因为上面用了Stream相关的API以及(Employee::getName)中::表示什么含义呢?别着急,慢慢往下看。

方法引用

使用操作符::将方法名和对象或者类的名字分隔开,组合有以下三种。

对象::实例方法名类::静态方法类::实例方法

常见的x-> System.out.println() 等同于System.out::println。 

注意:

Lambda 体中调用方法的参数列表与返回值类型,要与函数式接口抽象方法的该函数列表和返回值类型保持一致。若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时。可以使用 ClassName :: method

说起来比较抽象,请看下面的例子。

// 对象::实例方法 @Test public void testConsumer() { Employee emp = new Employee(); // 函数式接口Supplier是空参,返回是Integer类型的接口, // 而对应emp的实例方法getAge()刚刚好是空参且返回Integer类型的数据,符合上面注意事项1的要求 Supplier supplier = () -> emp.getAge(); Supplier sup2 = emp::getSalary; System.out.println(supplier.get()); } // 类::方法名 @Test public void testSupplier() { Comparator comparator = (x, y) -> x.compareTo(y); // 要求参数第一个值作为方法体的调用者,第二个参数值作为方法体的被调用者(参数)符合注意事项2的要求 Comparator compString = String::compareTo; System.out.println(comparator.compare("2", "3")); Comparator com = Integer::compare; System.out.println(com.compare(1, 2)); BiPredicate predicate = String::equals; System.out.println(predicate.test("we", "eq")); }

构造器引用

这儿以函数式接口为例:可以将返回的参数R,使用构造器的构造方法。

// 当Employee有下面的构造时候 public Employee(int age) { this.age = age; }

可构造以下这样的一个函数式接口。

Function fun = i -> new Employee(i) System.out.println(fun.apply(13)); // 使用构造器引用简化后: Function fun = Employee::new System.out.println(fun.apply(15));

到此Lambda简单入门结束,下一篇将介绍java8新特性之Stream相关API。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3