前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 8 函数式接口使用示例及相关新特性

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

作者头像
chenchenchen
发布2020-05-26 17:20:58
1.5K0
发布2020-05-26 17:20:58
举报
文章被收录于专栏:chenchenchenchenchenchen

简介

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

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

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

3、四种类型

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

这里写图片描述
这里写图片描述
  • Function: 接收参数,并返回结果,主要方法 R apply(T t)
  • Consumer: 接收参数,无返回结果, 主要方法为 void accept(T t)
  • Supplier: 不接收参数,但返回结构,主要方法为 T get()
  • Predicate: 接收参数,返回boolean值,主要方法为 boolean test(T t)

java.util.function包中所有的接口整理:

preview
preview

Function

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

接收单个参数

Interface

functional method

说明

Function<T,R>

R apply(T t)

接收参数类型为T,返回参数类型为R

IntFunction<R>

R apply(int value)

以下三个接口,指定了接收参数类型,返回参数类型为泛型R

LongFunction<R>

R apply(long value)

Double<R>

R apply(double value)

ToIntFunction<T>

int applyAsInt(T value)

以下三个接口,指定了返回参数类型,接收参数类型为泛型T

ToLongFunction<T>

long applyAsLong(T value)

ToDoubleFunction<T>

double applyAsDouble(T value)

IntToLongFunction

long applyAsLong(int value)

以下六个接口,既指定了接收参数类型,也指定了返回参数类型

IntToDoubleFunction

double applyAsLong(int value)

LongToIntFunction

int applyAsLong(long value)

LongToDoubleFunction

double applyAsLong(long value)

DoubleToIntFunction

int applyAsLong(double value)

DoubleToLongFunction

long applyAsLong(double value)

UnaryOperator<T>

T apply(T t)

特殊的Function,接收参数类型和返回参数类型一样

IntUnaryOperator

int applyAsInt(int left, int right)

以下三个接口,制定了接收参数和返回参数类型,并且都一样

LongUnaryOperator

long applyAsInt(long left, long right)

DoubleUnaryOperator

double applyAsInt(double left, double right)

接收两个参数

interface

functional 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)

BinaryOperator<T>

T apply(T t, T u)

特殊的BiFunction, 接收参数和返回参数类型一样

IntBinaryOperator

int applyAsInt(int left, int right)

LongBinaryOperator

long applyAsInt(long left, long right)

DoubleBinaryOperator

double applyAsInt(double left, double right)

Consumer

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

接收一个参数

interface

functional method

说明

Consumer<T>

void accept(T t)

接收一个泛型参数,无返回值

IntConsumer

void accept(int value)

以下三个类,接收一个指定类型的参数

LongConsumer

void accept(long value)

DoubleConsumer

void accept(double value)

接收两个参数

interface

functional method

说明

BiConsumer<T,U>

void accept(T t, U u)

接收两个泛型参数

ObjIntConsumer<T>

void accept(T t, int value)

以下三个类,接收一个泛型参数,一个指定类型的参数

ObjLongConsumer<T>

void accept(T t, long value)

ObjDoubleConsumer<T>

void accept(T t, double value)

Supplier

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

interface

functional method

说明

Supplier<T>

T get()

返回类型为泛型T

BooleanSupplier

boolean getAsBoolean()

以下三个接口,返回指定类型

IntSupplier

int getAsInt()

LongSupplier

long getAsLong()

DoubleSupplier

double getAsDouble()

Predicate

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

interface

functional method

说明

Predicate<T>

boolean test(T t)

接收一个泛型参数

IntPredicate

boolean test(int value)

以下三个接口,接收指定类型的参数

LongPredicate

boolean test(long value)

DoublePredicate

boolean test(double value)

BiPredicate<T,U>

boolean test(T t, U u)

接收两个泛型参数,分别为T,U

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

代码语言:javascript
复制
        Function<Integer, String> fun = new Function<Integer, String>() {
            @Override
            public String apply(Integer t) {
                return String.valueOf(t);
            }
        };

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

代码语言:javascript
复制
Function<Integer, String> fun = (x) -> String.valueOf(x);
String res = fun.apply(1000);
System.out.println(res); 

接口使用示例

一、Supplier供给型函数式接口

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

例:get方法使用

代码语言:javascript
复制
    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() + ""));
    }

输出:

代码语言:javascript
复制
2

1.2 有参数,返回一个结果

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

代码语言:javascript
复制
//生成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方法使用

代码语言:javascript
复制
    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));
    }

输出:

代码语言:javascript
复制
6

三、Function功能型函数式接口

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

代码语言:javascript
复制
package java.util.function;
import java.util.Objects;

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

    // 返回一个 先执行before函数对象apply方法,再执行当前函数对象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方法使用

代码语言:javascript
复制
/*
    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方法使用

代码语言:javascript
复制
    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

代码语言:javascript
复制
    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));
    }

输出:

代码语言:javascript
复制
true

例:test方法使用2

代码语言:javascript
复制
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

代码语言:javascript
复制
    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);
    }

输出:

代码语言:javascript
复制
输出所有数据:
1 2 3 4 5 6 7 8 9 
输出所有偶数:
2 4 6 8 
输出大于 3 的所有数字:
4 5 6 7 8 9 

例:test方法使用4

代码语言:javascript
复制
    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体的操作,已经有实现的方法了,可以使用方法引用!方法引用:使用操作符 ::将方法名和对象或类的名字分隔开来。如下三种主要使用情况

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

代码语言:javascript
复制
//静态方法
BinaryOperator<Double> binaryOperator = (x, y) -> Math.pow(x, y);
//等价于
BinaryOperator<Double> binaryOperator = Math::pow;
代码语言:javascript
复制
//实例方法: 类::实例方法
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,如下语句:

代码语言:javascript
复制
Function<String,Employee> f = (name)->new Employee(name);
//等价于↓
Function<String, Employee> f = Employee::new;

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

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

例如:

代码语言:javascript
复制
public interface MyFunction{
  void func();
  //声明一个接口的默认方法
  default void testDefalut(){
    System.out.println("MyFunction 默认方法");
  }
  //声明一个接口的静态方法
  static void testStatic(){
    System.out.println("MyFunction 静态方法");
  }
}
代码语言:javascript
复制
//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 静态方法
  */
}

接口冲突

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

代码语言:javascript
复制
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()方法。

参考:

https://segmentfault.com/a/1190000016596774

https://blog.csdn.net/z834410038/article/details/77370785

https://www.jianshu.com/p/86badf2c46c3

https://www.cnblogs.com/dgwblog/p/11739500.html

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-05-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
    • Function
      • 接收单个参数
      • 接收两个参数
    • Consumer
      • 接收一个参数
      • 接收两个参数
    • Supplier
      • Predicate
      • 接口使用示例
        • 一、Supplier供给型函数式接口
          • 二、Consumer消费型函数式接口
            • 三、Function功能型函数式接口
              • 四、Predicate断言型函数式接口
              • 相关新特性
                • 方法引用
                  • 构造方法
                    • 接口中的默认方法和静态方法
                    • 参考:
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档