函数式接口是为Java8中的lambda而设计的,lambda表达式的方法体就是函数接口的实现。
一、什么是函数式接口?
函数式接口是只包含一个方法的抽象接口。比如:Java标准库中的Java.lang.Runnable,java.util.Callable就是典型的函数式接口。
二、如何使用函数是接口?
在Java8中通过@FunctionalInterface进行注解,将一个接口的标注为函数式接口,该接口只包含一个抽象方法。 @Functionallnterface注解不是必须的,只要接口只包含一个抽象方法,虚拟机会自动判断该接口为函数式接口。 一般建议在接口上使用@Functionallnterface注解进行声明,以免他人错误的往接口中添加新的方法。当使用该注解后,如果在该接口中定义了第二个抽象方法的话,编译器会报错。
函数式接口为我们提供了四大内置核心函数式接口:
package Lambda;
import org.junit.Test;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/*
*Java8 内置的四大核心函数式接口
*
*Consumer<T> : 消费型接口
* void accept(T t);
*Supplier<T> : 供给型接口
* T get();
*Funcation<T,R> : 函数型接口
* R apply(T t);
*Predicate<T> : 断言型接口
* Boolean test(T t);
*
* */
public class FuncationCoreInterface {
// Consumer<T> : 消费型接口
@Test
public void fun1(){
happy(100000,(m) -> System.out.println("今天消费:" +m+"元。"));
}
public void happy(double money, Consumer<Double>consumer){
consumer.accept(money);
}
// Supplier<T> : 供给型接口
@Test
public void fun2(){
List<Integer> numList = getNumList(10,() -> (int)(Math.random()*100));
for (Integer num:numList) {
System.out.println(num);
}
}
// 需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for (int i =0;i<num;i++){
Integer n = sup.get();
list.add(n);
}
return list;
}
// Funcation<T,R> : 函数型接口
@Test
public void fun3(){
String s1 = strHandler("\t\t\t\t\t hdfs", (s) -> s.trim());
System.out.println(s1);
String s2 = strHandler("common", (s) -> s.substring(2, 4));
System.out.println(s2);
}
// 需求:用于处理字符串
public String strHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
// Predicate<T> : 断言型接口
@Test
public void fun4(){
List<String> list = Arrays.asList("hello", "lanbda", "world", "java", "ok");
List<String> list1 = filterStr(list, (s) -> s.length() <= 4);
for (String str:list1) {
System.out.println(str);
}
}
// 需求:将满足条件的字符串,放入到集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for (String str:list) {
if (pre.test(str)){
strList.add(str);
}
}
return strList;
}
}
三、什么是Lambda表达式?
Lambda表达式是一个匿名函数。可以将它看做匿名内部类,但从虚拟机的角度来看,并不是如此,因为Lambda表达式在编译的时候,并不会生成xxx$1.class的匿名类,而是通过动态绑定,在运行的时候再调用,因此避免了在编译时生成匆匆而影响jvm的加载速度。
四、怎么使用lambda表达式?
使用Lambda表达式的时候,接口必须是函数式接口。
基本语法:(<函数式接口> <变量名> = (参数1,参数2...) -> { //方法体 })
Java8中引入了一个新的操作符“->”该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两个部分:
左侧:Lambda表达式的参数列表。
右侧:Lambda表达式所需执行的内容,即Lambda体。
语法格式一:无参数,无返回值。() -> System.out.println("Hello Lambda");
package Lambda;
/*接口定义,无参无返回值*/
@FunctionalInterface
public interface ICompute {
public void print();
}
package Lambda;
public class testAnonymousInnerClass {
public static void main(String[] args) {
/*传统内部类测试*/
int i = 12;
hello(new ICompute() {
@Override
public void print() {
System.out.println(i);
System.out.println("Fuck");
}
});
/*lambda测试*/
hello(()-> System.out.println("Hello lambda"));
}
/*定义使用该接口的方法*/
private static void hello(ICompute iCompute) {
iCompute.print();
}
}
语法格式二:有一个参数,并且无返回值。(x) -> System.out.println(x);
package Lambda;
@FunctionalInterface
public interface HelloInterface {
void sayHello(String message);
}
package Lambda;
public class Test {
public static void main(String[] args) {
sayHello("peter",x-> System.out.println("Hello "+x));
}
private static void sayHello(String message,HelloInterface helloInterface){
helloInterface.sayHello(message);
}
}
语法格式三:若只有一个参数,小括号可以省略不写。x -> System.out.println(x);
语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句。
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
语法格式五:若Lambda体重只有一条语句,return和大括号都可以省略不写。
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
语法格式六 : Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推 断”
(Integer x, Integer y) -> Integer.compare(x, y);
注 : Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”
上联 : 左右遇一括号省 下联 : 左侧推断类型省 横批 : 能省则省
package Lambda;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
public class LambdaParam {
@Test
public void test1(){
// 无参数,无返回值
final int num = 0; //jdk 1.7前,必须是final
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("HelloWorld" + num);
}
};
System.out.println("========我是分割线=========");
Runnable r1 = () -> System.out.println("HelloLambda");
}
// 一个参数,无返回值
@Test
public void test2(){
Consumer<String> con = (x) -> System.out.println(x);
con.accept("哈哈哈哈");
Consumer<String> con1 = x -> System.out.println(x);
con1.accept("啧啧啧啧");
}
// 有两个参数,有返回值,Lambda体中有多条语句
@Test
public void test3(){
Comparator<Integer> com = (x,y) -> {
System.out.println("函数式接口");
return Integer.compare(x,y);
};
// 如果只有一条语句,大括号和return都可以不写
Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
}
// Lambda表达式的参数列表可以省略不写,因为jvm编译器可以自动推断出参数类型
@Test
public void test4(){
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
Comparator<Integer> com1 = (Integer x,Integer y) -> Integer.compare(x,y);
}
}
Lambda表达式的应用:
需求:
1、获取公司中员工年龄大于35岁的信息。
2、获取当前公司中员工工资大于5000的员工信息。
//实体类
package Lambda.LambdaCase;
public class Employee {
private String name;
private Integer age;
private Double salary;
public Employee() {
}
public Employee(String name, Integer age, Double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
//最原始的方法
package Lambda.LambdaCase;
import org.junit.Test;
import java.util.*;
public class TestLambda {
// 需求:获取公司中员工年龄大于35的员工信息
List<Employee> employees = Arrays.asList(
new Employee("张三",18,9999.99),
new Employee("李四",19,8888.88),
new Employee("王五",100,1.11),
new Employee("赵六",80,2.22),
new Employee("田七",20,9898.98)
);
@Test
public void test3(){
List<Employee> list = filterEmployees1(employees);
for (Employee emp:list) {
System.out.println(emp);
}
System.out.println("============我是分割线===========");
List<Employee> list1 = filterEmployees2(employees);
for (Employee emp:list1) {
System.out.println(emp);
}
}
public List<Employee> filterEmployees1(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp:list) {
if (emp.getAge() >= 35){
emps.add(emp);
}
}
return emps;
}
// 需求:获取当前公司中员工工资大于5000的员工信息,和上面的程序相差不多,产生大量的重复代码
public List<Employee> filterEmployees2(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp:list) {
if (emp.getSalary()>=5000) {
emps.add(emp);
}
}
return emps;
}
}
//优化方式一:对Employee集合按照Predicate的方式进行过滤(策略设计模式)
@Test
public void test4(){
List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());
for (Employee employee:list) {
System.out.println(employee);
}
System.out.println("============我是分割线===========");
List<Employee> list1 = filterEmployee(employees, new FilterEmployeeBySalary());
for (Employee employee:list1) {
System.out.println(employee);
}
}
public List<Employee> filterEmployee(List<Employee>list,MyPredicate<Employee> mp){
List<Employee> emps = new ArrayList<>();
for (Employee emp:list) {
if (mp.test(emp)){
emps.add(emp);
}
}
return emps;
}
//需求1
package Lambda.LambdaCase;
public class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge()>=35;
}
}
//需求二
package Lambda.LambdaCase;
public class FilterEmployeeBySalary implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getSalary()>=35;
}
}
//定义接口
package Lambda.LambdaCase;
public interface MyPredicate<T> {
public boolean test(T t);
}
//优化方式二:匿名内部类,省略创建的类MyPredicate
@Test
public void test5(){
List<Employee> list = filterEmployee(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() <= 5000;
}
});
for (Employee employee:list) {
System.out.println(employee);
}
}
//优化方式三:Lanbda表达式
@Test
public void test6(){
List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() <= 5000);
list.forEach(System.out::println);
}
//优化方式四:Stream API
@Test
public void test7(){
employees.stream()
.filter((e) -> e.getSalary() >= 5000)
.limit(2)
.forEach(System.out::println);
System.out.println("============我是分割线===========");
employees.stream()
.map(Employee::getName)
.forEach(System.out::println);
}