乐趣区

关于java:Java8中你可能不知道的一些地方之函数式接口实战

什么时候能够应用 Lambda?通常 Lambda 表达式是用在函数式接口上应用的。从 Java8 开始引入了函数式接口,其阐明比较简单:函数式接口 (Functional Interface) 就是一个有且仅有一个形象办法,然而能够有多个非形象办法的接口。java8 引入 @FunctionalInterface 注解申明该接口是一个函数式接口。

一、语法

  • 形象办法有且仅有一个
  • 接口应用 @FunctionalInterface 注解进行标注
  • 接口中能够存在默认办法和静态方法实现

如下模式:

/**
* 定义函数式接口
* 接口上标注 @FunctionalInterface 注解
*/
@FunctionalInterface
public interface ICollectionService {
   /**
    * 定义打印办法
    */
   void print();}

在 Java8 以前,已有大量函数式接口模式的接口(接口中只存在一个形象办法),只是没有强制申明。例如 java.lang.Runnable,java.util.concurrent.Callable,java.security.PrivilegedAction,java.io.FileFilter 等,Java8 新减少的函数接口在 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。

对于 Java8 中提供的这么多函数式接口,开发中罕用的函数式接口有以下几个 Predicate,Consumer,Function,Supplier。

二、函数式接口实例

2.1 Predicate

java.util.function.Predicate<T> 接口定义了一个名叫 test 的形象办法,它承受泛型 T 对象,并返回一个 boolean 值。在对类型 T 进行断言判断时,能够应用这个接口。通常称为断言型接口。

  • 字符串判空
Predicate<String> p01=(str)->str.isEmpty()||str.trim().isEmpty();
    /**
     * 测试传入的字符串是否为空
     */
   System.out.println(p01.test(""));
   System.out.println(p01.test(" "));
   System.out.println(p01.test("admin"));
  • 用户合法性校验

接口静态方法实现手机号非法校验性能,办法返回函数式接口 Predicate

public interface MyStringInter {public final  String checkPhone= "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(16[0-9])" +
           "|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
   /**
    * 用户手机格局合法性
    *     返回 L 函数式接口 Predicate 的实现 Lambda 表达式
    * @return
    */
   static Predicate<String> checkPhone(){return (e)-> {return Pattern.compile(checkPhone).matcher(e).matches();};
  }
}

2.2 Consumer

java.util.function.Consumer<T> 接口定义了一个名叫 accept 的形象办法,它承受泛型 T,没有返回值(void)。如果须要拜访类型 T 的对象,并对其执行某些操作,能够应用这个接口,通常称为消费型接口。

  • 热销商品展现
/**
 热销商品测试数据
*/
Goods g01=new Goods(1,"iPad 2018 款",3000,180, BigDecimal.valueOf(2300));
Goods g02=new Goods(6,"小米平板 4",5000,600, BigDecimal.valueOf(1900));
Goods g03=new Goods(9,"微软 Surface Pro 6",100,50, BigDecimal.valueOf(8500));
Goods g04=new Goods(20,"华为 光荣平板 5",1600,480, BigDecimal.valueOf(1500));
List<Goods> goods= Arrays.asList(g01,g02,g03,g04);


//Consumer 实现汇合数据输入 Lambda 代替匿名函数 实现 Consumer 接口
goods.forEach(g->{System.out.println(g);
});

2.3 Function

java.util.function.Function<T, R> 接口定义了一个叫作 apply 的办法,它承受一个泛型 T 的对象,并返回一个泛型 R 的对象。如果须要定义一个 Lambda,将输出的信息映射到输入,能够应用这个接口(比方提取苹果的分量,或把字符串映射为它的长度), 通常称为功能型接口。

  • 用户明码 Base64 编码
// 实现用户明码 Base64 加密操作
Function<String,String> f01=(password)->Base64.getEncoder().encodeToString(password.getBytes());
// 输入加密后的字符串
System.out.println(f01.apply("123456"));

2.4 Supplier

java.util.function.Supplier<T> 接口定义了一个 get 的形象办法,它没有参数,返回一个泛型 T 的对象,这相似于一个工厂办法, 通常称为功能型接口。

  • 内部 Properties 文件读取
public static Properties readFile(String fileName) {Supplier<Properties> supplier = () -> {
           try {InputStream is = TestCase04.class.getClassLoader().getResourceAsStream(fileName);
               Properties prop = new Properties();
               prop.load(is);
               return prop;
          } catch (IOException e) {e.printStackTrace();
               return null;
          }
      };
       return supplier.get();}

三、高阶函数

Java8 中函数式接口中办法容许函数接口作为办法形参传入,同时办法的后果为函数接口,从而实现链式调用操作,就像俄罗斯套娃那样,当把套娃一个个关上时,发现还有一个同样的小套娃在外面,最终发现最外面的一个也是一个残缺的套娃玩具,此时的高阶函数是不是跟套娃有着惊人的相似之处呢。

  • 多页面转发
String action = "";
Predicate<String> p01 = (a) -> StringUtils.isBlank(a);
/**
* 如果 action 为空 或 index 或 main 转发到网站主页面
* 链式判断 办法后果依然为一个函数
*/
if (p01.or((a) -> a.equals("index")).or((a) -> a.equals("main")).test(action)) {System.out.println("网站主页面...");
} else {System.out.println("其余页面...");
}
  • 多条件排序

这里以商品数据为例,按商品销量、评论排序,如果销量统一依照商品评论数排序

/**
* 理论开发数据通常从数据库获取
* 这里应用测试数据
*/
Goods g01=new Goods(1,"小米 9",1789,200, BigDecimal.valueOf(2500));
Goods g02=new Goods(2,"华为 Mate20",5000,3000, BigDecimal.valueOf(7000));
Goods g03=new Goods(3,"OPPO R17",2000,2827, BigDecimal.valueOf(1500));
Goods g04=new Goods(4,"魅族 Note9",2000,1600, BigDecimal.valueOf(1600));
Goods g05=new Goods(5,"一加 6T",8000,5000, BigDecimal.valueOf(3500));
List<Goods> goods= Arrays.asList(g01,g02,g03,g04,g05);


// 销量 与 评论排序 高阶函数应用
Comparator<Goods> comparator = (g1,g2)->g1.getSale()-g2.getSale();
goods.sort(comparator.thenComparing(Comparator.comparing(g3 -> g3.getComment())));
goods.forEach((g)->System.out.println(g));

高阶函数利用场景较多(这里查看源码相干高阶函数办法),如 Optinal 接口 filter、map、orElseGet 等办法,Stream 流操作等根本都会用到 Predicate, Consumer, Supplier, Function 等接口。

四、函数式接口劣势与利用场景

函数式接口的引入,联合 Lambda 的应用,打消的匿名函数繁琐的代码,使得代码构造简洁、紧凑,第二点就是函数式接口中应用高阶函数,能够很不便的实现链式调用,代码清晰简洁,同时引入的一种新的开发思维 - 函数式编程,对于开发者来说只须要关注函数的规定设计实现即可。

对于函数式接口利用,后续介绍到的 Optinal、Stream 相干办法对于数据处理的应用频率较高,同时也是形成函数式编程的核心内容。

退出移动版