Java 8 函数式接口使用示例及相关新特性

Java 8 函数式接口使用示例及相关新特性

简介

函数式接口(Functional Interface)是Java 8对一类特殊类型的接口的称呼。 函数式接口下共有

1、三种方法

  • 唯一的抽象方法

  • 使用default定义普通方法(默认方法),通过对象调用。 实现接口后,因为默认方法不是抽象方法,所以可以不重写,但是如果开发需要,也可以重写 。当然如果接口中的默认方法不能满足某个实现类需要,那么实现类可以覆盖默认方法。签名跟接口default方法一致,但是不能再加default修饰符。

  • 使用static定义静态方法,通过接口名调用。

2、一个新注解

  • 如果现在某一个接口就是为了函数式接口而生的,定义的时候就让其只有一个抽象方法,所以有了一个新的注解:函数式接口@FunctionInterface

3、四种类型

在jdk8中,引入了一个新的包java.util.function, 可以使java 8 的函数式编程变得更加简便。这个package中的接口大致分为了以下四类:

名称描述
Function<T,R>接受几个输入参数,返回一个结果
Consumer<T>代表接受一个输入参数并且无返回的操作
Predicate接收一个输入参数,返回一个布尔值结果
Supplier无参数,返回一个结果
  • Function: 接收参数,并返回结果,主要方法 R apply(T t)
  • Consumer: 接收参数,无返回结果, 主要方法为 void accept(T t)
  • Supplier: 不接收参数,但返回结构,主要方法为 T get()
  • Predicate: 接收参数,返回boolean值,主要方法为 boolean test(T t)

Function

表示一个方法接收参数并返回结果。

接收单个参数

Interfacefunctional method说明
Function<T,R>R apply(T t)接收参数类型为T,返回参数类型为R
IntFunctionR apply(int value)以下三个接口,指定了接收参数类型,返回参数类型为泛型R
LongFunctionR apply(long value)
DoubleR apply(double value)
ToIntFunctionint applyAsInt(T value)以下三个接口,指定了返回参数类型,接收参数类型为泛型T
ToLongFunctionlong applyAsLong(T value)
ToDoubleFunctiondouble applyAsDouble(T value)
IntToLongFunctionlong applyAsLong(int value)以下六个接口,既指定了接收参数类型,也指定了返回参数类型
IntToDoubleFunctiondouble applyAsLong(int value)
LongToIntFunctionint applyAsLong(long value)
LongToDoubleFunctiondouble applyAsLong(long value)
DoubleToIntFunctionint applyAsLong(double value)
DoubleToLongFunctionlong applyAsLong(double value)
UnaryOperatorT apply(T t)特殊的Function,接收参数类型和返回参数类型一样
IntUnaryOperatorint applyAsInt(int left, int right)以下三个接口,制定了接收参数和返回参数类型,并且都一样
LongUnaryOperatorlong applyAsInt(long left, long right)
DoubleUnaryOperatordouble applyAsInt(double left, double right)

接收两个参数

interfacefunctional method说明
BiFunction<T,U,R>R apply(T t, U u)接收两个参数的Function
ToIntBiFunction<T,U>int applyAsInt(T t, U u)以下三个接口,指定了返回参数类型,接收参数类型分别为泛型T, U
ToLongBiFunction<T,U>long applyAsLong(T t, U u)
ToDoubleBiFunction<T,U>double appleyAsDouble(T t, U u)
BinaryOperatorT apply(T t, T u)特殊的BiFunction, 接收参数和返回参数类型一样
IntBinaryOperatorint applyAsInt(int left, int right)
LongBinaryOperatorlong applyAsInt(long left, long right)
DoubleBinaryOperatordouble applyAsInt(double left, double right)

Consumer

表示一个方法接收参数但不产生返回值。

接收一个参数

interfacefunctional method说明
Consumervoid accept(T t)接收一个泛型参数,无返回值
IntConsumervoid accept(int value)以下三个类,接收一个指定类型的参数
LongConsumervoid accept(long value)
DoubleConsumervoid accept(double value)

接收两个参数

interfacefunctional method说明
BiConsumer<T,U>void accept(T t, U u)接收两个泛型参数
ObjIntConsumervoid accept(T t, int value)以下三个类,接收一个泛型参数,一个指定类型的参数
ObjLongConsumervoid accept(T t, long value)
ObjDoubleConsumervoid accept(T t, double value)

Supplier

返回一个结果,并不要求每次调用都返回一个新的或者独一的结果

interfacefunctional method说明
SupplierT get()返回类型为泛型T
BooleanSupplierboolean getAsBoolean()以下三个接口,返回指定类型
IntSupplierint getAsInt()
LongSupplierlong getAsLong()
DoubleSupplierdouble getAsDouble()

Predicate

根据接收参数进行断言,返回boolean类型

interfacefunctional method说明
Predicateboolean test(T t)接收一个泛型参数
IntPredicateboolean test(int value)以下三个接口,接收指定类型的参数
LongPredicateboolean test(long value)
DoublePredicateboolean test(double value)
BiPredicate<T,U>boolean test(T t, U u)接收两个泛型参数,分别为T,U

备注:关于lambda表达式 JDK8以前,通过匿名内部类可以实现接口

        Function<Integer, String> fun = new Function<Integer, String>() {
            @Override
            public String apply(Integer t) {
                return String.valueOf(t);
            }
        };

JDK8中,通过lambda表达式实现,可以得出一个结论,lambda表达式就是为了优化匿名内部类而生。

Function<Integer, String> fun = (x) -> String.valueOf(x);
String res = fun.apply(1000);
System.out.println(res); 

接口使用示例

一、Supplier供给型函数式接口

1.1 无参数,返回一个结果。

例:get方法使用

    public static String supplierTest(Supplier<String> supplier) {  
        return supplier.get();  
    }  

    public static void main(String args[]) {
        String name = "冷冷";
        // () -> name.length() 无参数,返回一个结果(字符串长度)
        // 所以该lambda表达式可以实现Supplier接口
        System.out.println(supplierTest(() -> name.length() + ""));
    }

输出:

2

复制

1.2 有参数,返回一个结果

例:生成10个100以内的随机数

//生成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;
}

public static void main(String[] args) {
    //10个100以内的随机数
    List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
    for (Integer num : numList) {
        System.out.println(num);
    }
}

二、Consumer消费型函数式接口

代表了 接受一个输入参数并且无返回的操作

例:accept方法使用

    public static void modifyTheValue3(int value, Consumer<Integer> consumer) {
        consumer.accept(value);
    }

    public static void main(String[] args) {
        // (x) -> System.out.println(x * 2)接受一个输入参数x
        // 直接输出,并没有返回结果
        // 所以该lambda表达式可以实现Consumer接口
        modifyTheValue3(3, (x) -> System.out.println(x * 2));
    }

输出:

6

三、Function功能型函数式接口

Function接口 接受一个输入参数T,返回一个结果R。

package java.util.function;
import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {
    // 接受输入参数,对输入执行所需操作后  返回一个结果。
    R apply(T t);

    // 返回一个 pply方法,再执行当前函数对象apply方法的 函数对象。
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
       Objects.requireNonNull(before);
       return (V v) -> apply(before.apply(v));
    }

    // 返回一个 先执行当前函数对象apply方法, 再执行after函数对象apply方法的 函数对象。
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }   

    // 返回一个执行了apply()方法之后只会返回输入参数的函数对象。
    static <T> Function<T, T> identity() {
        return t -> t;
    } 
}

例:apply方法使用

/*
    Function接口常用于数据的处理转换,比如给定一个员工列表,需要返回名称列表
*/
public class Employee {
    private int id;
    private String name;
    private double salary;
    public Employee(String name){
        this.name = name;
    }
    public Employee(String name,double salary) {
        this.name = name;
        this.salary = salary;
    }
    //省略getter setter
}
public class TestEmp{

    public static <T, R>List<R> map(List<T> list,Function<T, R> fun){
        List<R> returnList = new ArrayList<>(list.size());
        for (T e : list) {
            returnList.add(fun.apply(e));
        }
        return returnList
    }

    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(new Employee("老张"),
                new Employee("小李"),
                new Employee("老王"),
                new Employee("小刘"),
                new Employee("小胖"));
        List<String> nameList = map(employees, (employee -> employee.getName()));
        System.out.println(nameList);
        /*
            console:[老张, 小李, 老王, 小刘, 小胖]
        */
    }
}

例:andThen方法使用

    public static Integer modifyTheValue2(int value, Function<Integer, Integer> function1, Function<Integer, Integer> function2){
         //value作为function1的参数,返回一个结果,该结果作为function2的参数,返回一个最终结果
         return  function1.andThen(function2).apply(value);
    }

    public static void main(String[] args) {
        System.out.println(modifyTheValue2(3, val -> val + 2, val -> val + 3));	
    }

四、Predicate断言型函数式接口

接受一个输入参数,返回一个布尔值结果。

例:test方法使用1

    public static boolean predicateTest(int value, Predicate<Integer> predicate) {
        return predicate.test(value);
    }

    public static void main(String[] args) {
        // (x) -> x == 3 输入一个参数x,进行比较操作,返回一个布尔值
        // 所以该lambda表达式可以实现Predicate接口
        System.out.println(predicateTest(3, (x) -> x == 3));
    }

输出:

true

例:test方法使用2

public static <E> List<E> filter(List<E> list, Predicate<E> pred) {
    List<E> retList = new ArrayList<>();
        for (E e : list) {
            if (pred.test(e)) {
                retList.add(e);
            }
        }
    return retList;
}
public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(new Employee("老张"),
                new Employee("小李", 3000.00),
                new Employee("老王", 5000.00),
                new Employee("小刘", 7000.00),
                new Employee("小胖", 10000.00));
        //过滤薪资小于5000的员工
        List<Employee> filter = filter(employees,
                                       employee -> employee.getSalary() > 5000.00);
        for (Employee employee : filter) {
            System.out.println(employee.getName() + ":" + employee.getSalary());
        }
        /*
            console:小刘:7000.0
                    小胖:10000.0
        */
}

例:test方法使用3

    public static void eval(List<Integer> list, Predicate<Integer> predicate) {
        for (Integer n : list) {
            if (predicate.test(n)) {
                System.out.print(n + " ");
            }
        }

//      list.forEach(n -> {
//          if (predicate.test(n)) {
//              System.out.print(n + " ");
//          }
//      });
    }

    public static void main(String args[]) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        // Predicate<Integer> predicate = n -> true
        // n 是一个参数传递到 Predicate 接口的 test 方法
        // n 如果存在则 test 方法返回 true

        System.out.println("输出所有数据:");

        // 传递参数 n
        eval(list, n -> true);

        // Predicate<Integer> predicate1 = n -> n%2 == 0
        // n 是一个参数传递到 Predicate 接口的 test 方法
        // 如果 n%2 为 0 test 方法返回 true

        System.out.println("\n输出所有偶数:");
        eval(list, n -> n % 2 == 0);

        // Predicate<Integer> predicate2 = n -> n > 3
        // n 是一个参数传递到 Predicate 接口的 test 方法
        // 如果 n 大于 3 test 方法返回 true

        System.out.println("\n输出大于 3 的所有数字:");
        eval(list, n -> n > 3);
    }

复制

输出:

输出所有数据:
1 2 3 4 5 6 7 8 9 
输出所有偶数:
2 4 6 8 
输出大于 3 的所有数字:
4 5 6 7 8 9 

复制

例:test方法使用4

    public static boolean validInput(String name, Predicate<String> function) {  
        return function.test(name);  
    }  

    public static void main(String args[]) {
        String name = "冷冷";
        if(validInput(name, s -> !s.isEmpty() &&  s.length() <= 3 )) {
            System.out.println("名字输入正确");
        }
    }

复制

相关新特性

方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!方法引用:使用操作符 ::将方法名和对象或类的名字分隔开来。如下三种主要使用情况

对象 : : 实例方法 类 : : 静态方法 类 : : 实例方法 基本用法 例如:

//静态方法
BinaryOperator<Double> binaryOperator = (x, y) -> Math.pow(x, y);
//等价于
BinaryOperator<Double> binaryOperator = Math::pow;

复制

//实例方法: 类::实例方法
Function<Employee, String> f = (Employee e) -> e.getName();
//等价于
Function<Employee, String> f = Employee::getName;
//---------------------------------------------------------
//对象::实例方法
Employee e = new Employee("小李", 3000.00);
Supplier<String> s = () -> e.getName();
//等价于↓
Supplier<String> s = e::getName;

复制

构造方法

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数 列表要与接口中抽象方法的参数列表一致!对于构造方法,方法引用的语法是<类名>::new,如Employee::new,如下语句:

Function<String,Employee> f = (name)->new Employee(name);
//等价于↓
Function<String, Employee> f = Employee::new;

复制

接口中的默认方法和静态方法

Java8以前,接口里的方法要求全部是抽象方法,Java8以后允许在接口里定义默认方法静态方法,默认方法使用 default 关键字修饰。默认方法的主要优势是提供一种拓展接口的方法,而不破坏现有代码。

例如:

public interface MyFunction{
  void func();
  //声明一个接口的默认方法
  default void testDefalut(){
    System.out.println("MyFunction 默认方法");
  }
  //声明一个接口的静态方法
  static void testStatic(){
    System.out.println("MyFunction 静态方法");
  }
}

复制

//MyFunctionImpl实现接口MyFunction
public class MyFunctionImpl implements MyFunction {
    @Override
    public void func() {
        System.out.println("实现抽象方法");
    }

    public static void main(String[] args) {
        MyFunction my = new MyFunctionImpl();
        my.func();
        my.testDefalut();
        MyFunction.testStatic();
    }
  /*
            实现抽象方法
            MyFunction 默认方法
            MyFunction 静态方法
  */
}

复制

接口冲突

如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

public interface AnotherFunction {
    default void testDefalut() {
        System.out.println("AnotherFunction 默认方法");
    }
}

public class FunctionImpl implements MyFunction,AnotherFunction{
    @Override
    public void func() {
        System.out.println(" FunctionImpl 实现抽象方法");
    }

    @Override
    public void testDefalut() {
         System.out.println(" FunctionImpl 覆盖接口中默认方法解决冲突");
    }
}

复制

如果不覆盖接口中相同的默认方法,那么new MyFunctionImpl().testDefalut();中调用的testDefalut方法到底是哪个接口的testDefalut()方法呢?所以必须在实现类中覆盖testDefalut()方法。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TyRed08

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值