Lambda 表达式是Java8以后的新特性,Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。我们以线程(实现Runnable接口)为例,,看使用Lambda和不适用Lambda的区别。
package com.alian.csdn.lambda;
import org.junit.Test;
public class ThreadWithLambdaTest {
/**
* Java 8之前实现一个线程的简单写法
* 此处单元测试的注解是采用:org.junit.Test
*/
@Test
public void oldThread() {
// Java 8之前实现一个线程的写法
final String str = "before Java 8";
new Thread(new Runnable() {
@Override
public void run() {
doSomeThing(str);
}
}).start();
}
/**
* Java 8开始使用Lambda实现一个线程的简单写法
* 此处单元测试的注解是采用:org.junit.Test
*/
@Test
public void newThread() {
final String str = "Lambda";
//Java 8开始使用Lambda:相当于自动重写了run方法
new Thread(() -> doSomeThing(str)).start();
}
private void doSomeThing(String str) {
System.out.println("1.执行开始");
System.out.println("2.执行线程,来源于:" + str);
System.out.println("3.执行结束");
}
}
Java 8开始使用Lambda:相当于自动重写了run方法,与原来的版本写法相比,Lambda写法简单( 更适合匿名函数或匿名内部类),代码量更少,但是如果不理解写法的话,同时也会增加团队里代码可读性。
四大语法特征的详细的说明如下:
package com.alian.csdn.lambda;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
public class LambdaTest {
@Test
public void lambda() {
//定义一个列表
List<String> listStr = Arrays.asList("bbb", "ddd", "cccc", "aa");
//1.此处p的类型就是由编译器推理得到的,不需要声明类型,当然也可以声明类型,你可以换成你想取的任意的合规名称
listStr.forEach(p -> System.out.println("1.列表里的值:" + p));
//2.可选的参数圆括号(无圆括号),一个参数可以不写圆括号
listStr.forEach(p -> System.out.println("2.1列表里的值(无圆括号):" + p));
//2.可选的参数圆括号(有圆括号)
listStr.forEach((p) -> System.out.println("2.2列表里的值(有圆括号):" + p));
//2.可选的参数圆括号,两个及以上的参数需要加圆括号
listStr.sort((e1, e2) -> {
System.out.println("两个及以上的参数需要加圆括号");
return e1.compareTo(e2);
});
//3.可选的大括号(无大括号):如果主体只包含了一个语句,可以不使用大括号
listStr.forEach(p -> System.out.println("3.1列表里的值(无大括号):" + p));
//3.可选的大括号(有大括号)
listStr.forEach(p -> {
System.out.println("3.1列表里的值(有大括号):" + p);
});
//3.可选的大括号,两个及以上语句需要用大括号
listStr.forEach(p -> {
System.out.print("休息下:");
System.out.println("3.1列表里的值(有大括号):" + p);
});
//4.可选的返回关键字(一个表达式编译器会自动返回值)
listStr.sort(String::compareTo);
//4.可选的返回关键字(大括号需要指明表达式返回了一个数值,一般是多条语句使用大括号)
listStr.sort((e1, e2) -> {
System.out.println("大括号需要指明表达式返回了一个数值");
return e1.compareTo(e2);
});
}
}
在本文测试类的同级目录下的Employee类 ,用于接下里的测试
package com.alian.csdn.lambda;
import lombok.Data;
/**
* 省篇幅,偷懒就用@Data完事算了!
*/
@Data
public class Employee {
private String id;//员工ID
private String name;//员工姓名
private String department;//部门
private int age;//员工年龄
private double salary;//工资
/*
* 简单的构造方法用于测试
*/
public Employee(String id, String name, String department, int age, double salary) {
this.id = id;
this.name = name;
this.department = department;
this.age = age;
this.salary = salary;
}
}
消费型接口就是传进去参数经过表达式处理,没有返回值
Consumer
3.1.1、源码
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
3.1.2、实战
我们从上面也看到了消费型接口Consumer,接收的是一个泛型,可以是String,Integer,对象(比如Employee)等等,大胆发挥你的想象即可,不要被网上的事例限制了思维,认为只有String等简单类型
package com.alian.csdn.lambda;
import org.junit.Test;
import java.util.function.Consumer;
public class ConsumerTest {
/**
* 消费型接口:Consumer
* 此处单元测试的注解是采用:org.junit.Test
*/
@Test
public void consumer() {
System.out.println("-----------------Consumer------------------------" );
//消费型接口直接传入参数,进行相关处理,没有返回值
consumerString("我是消费型接口", m -> {
System.out.println("1.我进入到方法体");
System.out.println("1.传入的参数输出:" + m);
});
System.out.println("-----------------Consumer的缩略写法------------------------" );
//传入参数未参与运算
consumerString("2.我是消费型接口,缩略写法", System.out::println);
System.out.println("-----------------Consumer------------------------" );
//我们有一个员工,现在把他调到 "销售二部" 同时 "加200元工资"
Employee employee = new Employee("BAT001", "胡昊天", "销售一部", 28, 3500);//在本类的同级目录下,见上文
consumerObeject(employee, m -> {
m.setDepartment("销售二部");//修改部门
m.setSalary(3700);//修改工资
System.out.println("员工信息:" + m);
});
}
public void consumerString(String str, Consumer<String> con) {
con.accept(str);
}
public void consumerObeject(Employee emp, Consumer<Employee> con) {
con.accept(emp);
}
}
运行结果:
-----------------Consumer---------------------------------
1.我进入到方法体
1.传入的参数输出:我是消费型接口
-----------------Consumer的缩略写法------------------------
2.我是消费型接口,缩略写法
-----------------Consumer-------------------------------
员工信息:Employee(id=BAT001, name=胡昊天, department=销售二部, age=28, salary=3700.0)
供给型接口就是无中生有,不传参经过表达式处理返回值,比如生成随机数
Supplier
3.2.1、源码
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
3.2.2、实战
package com.alian.csdn.lambda;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class SupplierTest {
/**
* 供给型接口:Supplier
* 此处单元测试的注解是采用:org.junit.Test
*/
@Test
public void supplier() {
//生成m到n随机数,包含m和n ,比如生成10个 15到25的随机数
List<Integer> numList = getNumList(10, () -> {
//第一步算出 m-n的值,假设等于w
//第二步Math.random()*w
//第三步Math.random()*w+n
//第四步Math.round(Math.random()w+n)
return (int) Math.round(Math.random() * (25 - 15) + 15);
});
System.out.println("获取到的10个[10,15]的随机数:"+numList);
}
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
list.add(sup.get());
}
return list;
}
}
运行结果:
获取到的10个[10,15]的随机数:[24, 16, 18, 23, 22, 20, 19, 24, 23, 19]
函数型接口就是传进去参数经过表达式处理后,有返回值。
Function
3.3.1、源码
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
3.3.2、实战
package com.alian.csdn.lambda;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
public class FunctionTest {
/**
* 函数型接口:Function
* 此处单元测试的注解是采用:org.junit.Test
*/
@Test
public void function() {
System.out.println("------------------Function----------------------------------------" );
String newStr = functionStr("csdnAlian_1223iszhebestman", (str) -> str.substring(4, 14));
System.out.println("字符串裁剪得到的结果:" + newStr);
System.out.println("------------------Function,Employee>------------------------------"
);
Employee[] employeeArray = {
new Employee("BAT005", "梁南生", "研发部", 27, 8000),
new Employee("BAT003", "唐二鹏", "研发部", 32, 9900),
new Employee("BAT004", "王一林", "研发部", 30, 9000),
new Employee("BAT006", "吴三雅", "财务部", 25, 6000)
};
//员工数组转化为List对象
List<Employee> list = Arrays.asList(employeeArray);
//从列表中找到最大年龄员工的信息
Employee employee = functionObject(list, emp -> {
//根据年龄进行倒序排序,默认是升序排序,使用reversed()可以进行倒序排序
emp.sort(Comparator.comparingInt(Employee::getAge).reversed());
return emp.get(0);
});
System.out.println("从列表中获取到的最大年龄的员工信息:" + employee);
}
public String functionStr(String str, Function<String, String> fun) {
return fun.apply(str);
}
public Employee functionObject(List<Employee> emp, Function<List<Employee>, Employee> fun) {
return fun.apply(emp);
}
}
运行结果:
------------------Function---------------------------------------------
字符串裁剪得到的结果:Alian_1223
------------------Function,Employee>-----------------------------------
从列表中获取到的最大年龄的员工信息:Employee(id=BAT003, name=唐二鹏, department=研发部, age=32, salary=9900.0)
断言型接口就是传进去参数经过表达式处理,有返回值(true或者false)
Predicate
3.4.1、源码
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
3.4.2、实战
package com.alian.csdn.lambda;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTest {
/**
* 断言型接口:Predicate
* 此处单元测试的注解是采用:org.junit.Test
*/
@Test
public void predicate() {
Employee[] employeeArray = {
new Employee("BAT005", "梁南生", "研发部", 27, 8000),
new Employee("BAT003", "唐二鹏", "研发部", 32, 9900),
new Employee("BAT004", "王一林", "研发部", 30, 9000),
new Employee("BAT006", "吴三雅", "财务部", 25, 6000)
};
//员工数组转化为List对象
List<Employee> list = Arrays.asList(employeeArray);
//找出列表里员工工资大于6000并且年龄小于31岁的员工的信息
List<Employee> returnList = filterStr2(list, (emp) -> (emp.getSalary() > 6000 && emp.getAge() < 31));
for (Employee emp : returnList) {
System.out.println("工资大于6000并且年龄小于31岁的员工的信息:" + emp);
}
}
public List<Employee> filterStr2(List<Employee> list, Predicate<Employee> pre) {
List<Employee> newList = new ArrayList<>();
for (Employee emp : list) {
if (pre.test(emp)) {
newList.add(emp);
}
}
return newList;
}
}
运行结果:
工资大于6000并且年龄小于31岁的员工的信息:Employee(id=BAT005, name=梁南生, department=研发部, age=27, salary=8000.0)
工资大于6000并且年龄小于31岁的员工的信息:Employee(id=BAT004, name=王一林, department=研发部, age=30, salary=9000.0)
以上就是今天要讲的内容,本文仅仅简单介绍了Lambda的四大语法特征,以及四大内置核心函数式接口:Consumer、Supplier、Funciton、Predicate的介绍和使用,同时这一节也是我们学好Java 8 Stream的一个基础,需要大家去仔细查看Java提供的API和不断的实践。如果有什么疑问,欢迎大家评论交流。