java8 新特性之函数式接口

参考文章:菜鸟教程 https://www.runoob.com/java/java8-functional-interfaces.html

 

函数式接口介绍(摘自菜鸟):

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

函数式接口可以被隐式转换为 lambda 表达式。

Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。

如定义了一个函数式接口如下:

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):

GreetingService greetService1 = message -> System.out.println("Hello " + message);

java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有(对于英文不好的我,这个表格也算是照抄下来了):

 

序号接口 & 描述
1 BiConsumer<T,U>

代表了一个接受两个输入参数的操作,并且不返回任何结果

2 BiFunction<T,U,R>

代表了一个接受两个输入参数的方法,并且返回一个结果

3 BinaryOperator<T>

代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

4 BiPredicate<T,U>

代表了一个两个参数的boolean值方法

5 BooleanSupplier

代表了boolean值结果的提供方

6 Consumer<T>

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

7 DoubleBinaryOperator

代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。

8 DoubleConsumer

代表一个接受double值参数的操作,并且不返回结果。

9 DoubleFunction<R>

代表接受一个double值参数的方法,并且返回结果

10 DoublePredicate

代表一个拥有double值参数的boolean值方法

11 DoubleSupplier

代表一个double值结构的提供方

12 DoubleToIntFunction

接受一个double类型输入,返回一个int类型结果。

13 DoubleToLongFunction

接受一个double类型输入,返回一个long类型结果

14 DoubleUnaryOperator

接受一个参数同为类型double,返回值类型也为double 。

15 Function<T,R>

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

16 IntBinaryOperator

接受两个参数同为类型int,返回值类型也为int 。

17 IntConsumer

接受一个int类型的输入参数,无返回值 。

18 IntFunction<R>

接受一个int类型输入参数,返回一个结果 。

19 IntPredicate

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

20 IntSupplier

无参数,返回一个int类型结果。

21 IntToDoubleFunction

接受一个int类型输入,返回一个double类型结果 。

22 IntToLongFunction

接受一个int类型输入,返回一个long类型结果。

23 IntUnaryOperator

接受一个参数同为类型int,返回值类型也为int 。

24 LongBinaryOperator

接受两个参数同为类型long,返回值类型也为long。

25 LongConsumer

接受一个long类型的输入参数,无返回值。

26 LongFunction<R>

接受一个long类型输入参数,返回一个结果。

27 LongPredicate

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

28 LongSupplier

无参数,返回一个结果long类型的值。

29 LongToDoubleFunction

接受一个long类型输入,返回一个double类型结果。

30 LongToIntFunction

接受一个long类型输入,返回一个int类型结果。

31 LongUnaryOperator

接受一个参数同为类型long,返回值类型也为long。

32 ObjDoubleConsumer<T>

接受一个object类型和一个double类型的输入参数,无返回值。

33 ObjIntConsumer<T>

接受一个object类型和一个int类型的输入参数,无返回值。

34 ObjLongConsumer<T>

接受一个object类型和一个long类型的输入参数,无返回值。

35 Predicate<T>

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

36 Supplier<T>

无参数,返回一个结果。

37 ToDoubleBiFunction<T,U>

接受两个输入参数,返回一个double类型结果

38 ToDoubleFunction<T>

接受一个输入参数,返回一个double类型结果

39 ToIntBiFunction<T,U>

接受两个输入参数,返回一个int类型结果。

40 ToIntFunction<T>

接受一个输入参数,返回一个int类型结果。

41 ToLongBiFunction<T,U>

接受两个输入参数,返回一个long类型结果。

42 ToLongFunction<T>

接受一个输入参数,返回一个long类型结果。

43 UnaryOperator<T>

接受一个参数为类型T,返回值类型也为T。

 

代码示例:

菜鸟教程的示例略

BiConsumer<T,U>

代表了一个接受两个输入参数的操作,并且不返回任何结果

// 想不到更好的示例了,就测试一下 BiConsumer  应该一次性单独逻辑,不依赖的
eval2(list, (k,v) -> {System.out.println("传入的参数1为:"+k + "  传入的参数2为:"+v);});

public static void eval2(List<Integer> list, BiConsumer<Integer,String> biConsumer) {
        for(Integer n: list) {biConsumer.accept(n,"pa2");}
    }

输出结果为:

复制代码
传入的参数1为:1  传入的参数2为:pa2
传入的参数1为:2  传入的参数2为:pa2
传入的参数1为:3  传入的参数2为:pa2
传入的参数1为:4  传入的参数2为:pa2
传入的参数1为:5  传入的参数2为:pa2
传入的参数1为:6  传入的参数2为:pa2
传入的参数1为:7  传入的参数2为:pa2
传入的参数1为:8  传入的参数2为:pa2
传入的参数1为:9  传入的参数2为:pa2
复制代码

BinaryOperator<T>

代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

//  代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
        // 逻辑处理,并返回与参数类型一样的结果
eval3(list, (k,v) -> v+k);

public static void eval3(List<Integer> list, BinaryOperator<String> binaryOperator) {
        for(Integer n: list) {System.out.println(binaryOperator.apply(String.valueOf(n),"abc"));}
}

这个方法泛型只有一个,却要传递两个参数。结果如下:

复制代码
abc1
abc2
abc3
abc4
abc5
abc6
abc7
abc8
abc9
复制代码

 

BiPredicate<T,U>

代表了一个两个参数的boolean值方法

复制代码
// BiPredicate<T,U> 方法介绍:代表了一个两个参数的boolean值方法
// 处理逻辑并返回boolean类型,可穿两个不同类型参数。
eval4(list, (k,v) -> v.length()==k);



public static void eval4(List<Integer> list, BiPredicate<Integer,String> biPredicate) {
        for(Integer n: list) {System.out.println(biPredicate.test(n,"213"));}
}
复制代码

这个可以传入两个不同的类型进行对比,返回boolean类型。结果如下:

复制代码
false
false
true
false
false
false
false
false
false
复制代码

BiFunction<T,U,R>

代表了一个接受两个输入参数的方法,并且返回一个结果

复制代码
// BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
// 三个类型都可自定义,相当于处理两个参数后返回结果。
eval5(list, (k,v) -> v.length()==k);



public static void eval5(List<Integer> list, BiFunction<Integer,String,Boolean> biFunction) {
     for(Integer n: list) {System.out.println(biFunction.apply(n,"213"));}
}
复制代码

结果如下:

复制代码
false
false
true
false
false
false
false
false
false
复制代码

 

代码没有具体含义,只是学习一下,其实只要看几个大概就能理解这个函数式接口的大概逻辑了。

有一些自带的函数式接口,除了唯一的一个抽象方法,还有一些默认方法,还是很好的。可以看下源码,暂时无业务需求。

 贴上完整的练习代码如下:

复制代码
package cn.wh.java8.chapter.one;

import java.util.Arrays;
import java.util.List;
import java.util.function.*;

public class FunctionInterface {

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


//        // 菜鸟示例:start
//        // 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("输出所有偶数:");
//        eval(list, n-> n%2 == 0 );
//
//        // Predicate<Integer> predicate2 = n -> n > 3
//        // n 是一个参数传递到 Predicate 接口的 test 方法
//        // 如果 n 大于 3 test 方法返回 true
//
//        System.out.println("输出大于 3 的所有数字:");
//        eval(list, n-> n > 3 );
//        // 菜鸟示例: end;

        // 想不到更好的示例了,就测试一下 BiConsumer  应该一次性单独逻辑,不依赖的
//        eval2(list, (k,v) -> {System.out.println("传入的参数1为:"+k + "  传入的参数2为:"+v);});

        //  代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
        // 逻辑处理,并返回与参数类型一样的结果
//        eval3(list, (k,v) -> v+k);

        // BiPredicate<T,U> 方法介绍:代表了一个两个参数的boolean值方法
        // 处理逻辑并返回boolean类型,可穿两个不同类型参数。
//        eval4(list, (k,v) -> v.length()==k);


        // BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
        // 三个类型都可自定义,相当于处理两个参数后返回结果。
        eval5(list, (k,v) -> v.length()==k);





    }




    public static void eval(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list) {if(predicate.test(n)) {System.out.println(n + " "); }}
    }
    public static void eval2(List<Integer> list, BiConsumer<Integer,String> biConsumer) {
        for(Integer n: list) {biConsumer.accept(n,"pa2");}
    }
    public static void eval3(List<Integer> list, BinaryOperator<String> binaryOperator) {
        for(Integer n: list) {System.out.println(binaryOperator.apply(String.valueOf(n),"abc"));}
    }
    public static void eval4(List<Integer> list, BiPredicate<Integer,String> biPredicate) {
        for(Integer n: list) {System.out.println(biPredicate.test(n,"213"));}
    }
    public static void eval5(List<Integer> list, BiFunction<Integer,String,Boolean> biFunction) {
        for(Integer n: list) {System.out.println(biFunction.apply(n,"213"));}
    }


}
FunctionInterface
复制代码

 

写完函数式接口,有个疑问函数式接口和抽象类有啥区别?除了接口和函数的区别,还有那些区别。以下摘自知乎,还不是很能说服自己。

在 java 8 之前,接口与其实现类之间的 耦合度 太高了(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。这在 lambda 表达式作为 java 8 语言的重要特性而出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。

  • 接口可以被类多实现(被其他接口多继承),抽象类只能被单继承。
  • 接口中没有 this,没有构造函数,不能拥有实例字段(实例变量)或实例方法,无法保存 状态state),抽象方法中可以。
  • 抽象类不能在 java 8 的 lambda 表达式中使用。
  • 从设计理念上,接口反映的是 “like-a” 关系,抽象类反映的是 “is-a” 关系。

作者:魔王不造反
链接:https://www.zhihu.com/question/68474140/answer/263792395
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 
就像评论里面说的,“当需要为一个接口添加方法时,所有的实现类都必须随之修改”,那就增加新的接口,而不是修改原来的,开闭原则可以了解一下。
 
 

摘自菜鸟教程的评论
1、Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
2、函数式接口里允许定义静态方法

  函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的;

3、函数式接口里允许定义 java.lang.Object 里的 public 方法

      函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了 Object 类,包含了来自 java.lang.Object 里对这些抽象方法的实现;

 
 
 
 
 
 
 
 
 
 
posted @   苦心明  阅读(509)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示