Java8新特性——函数式接口和Lambda表达式

      函数式接口是为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);
    }
发布了88 篇原创文章 · 获赞 383 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/weixin_42047611/article/details/81456874