JDK8新特性4——常用内置函数接口

1. 引言         

        使用Lambda表达式的前提是需要有函数式接口,而Lambda使用时不关心接口名,抽象方法名,只关心抽象方法的参数列表和返回值类型,因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。这些函数式接口都定义在java.util.function中。

在学习内置函数式接口之前,先自己定义一个函数式接口

需求:定义一个函数式接口Operation,在测试类中定义一个方法test,参数就是接口Operation

接口:

package com.hm.funcinter;

public interface Operation {
    void getSum(int a,int b);
}

测试类

package com.hm.funcinter.demo;

import com.hm.funcinter.Operation;

public class Demo01 {
    public static void main(String[] args) {
        test((a,b) -> System.out.println(a+b),3,5);
    }

    public static void test(Operation operation,int x,int y){
        operation.getSum(x,y);
    }
}

运行结果:8

2. 供给型接口——Supplier<T>

所谓供给型接口就是别人不给我们参数,但是我们返回一个类型值的接口方法,我们称之为供给型接口,也称之为生产者。

java.util.function.Supplier<T>接口仅包含一个无参的方法: T get() 。,对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。接口定义如下:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

通过Supplier的get方法,可以得到一个值,无参有返回的接口。

例如:使用Lambda表达式返回一个int数据中的最大值

package com.hm.funcinter.demo;

import java.util.Arrays;
import java.util.function.Supplier;

public class DemoSupplier {

    public static void main(String[] args) {
        int i = printMax(() -> {
            int[] intArr = {1, 4, 2, 5, 6, 3};
            Arrays.sort(intArr);
            return intArr[intArr.length - 1];
        });
        System.out.println(i);
    }

    public static int printMax(Supplier<Integer> supplier){
        return supplier.get();
    }
}

6.2 消费型——Consumer<T>

所谓的消费型就是需要一个参数,但是不给返回值的接口方法,称之为消费型接口,也称之为消费者。

java.util.function.Consumer<T>接口它就是一个消费型的接口,其数据类型由泛型参数来确定。

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {
    void accept(T var1);

    default Consumer<T> andThen(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        return (var2) -> {
            this.accept(var2);
            var1.accept(var2);
        };
    }
}

 从其定义可以看出,该接口有一个抽象方法accept和一个默认方法andThen。

6.2.1 抽象方法accept

Consumer接口的accept可以拿到accept方法参数传递过来的数据进行处理,是一个有参数无返回值的方法。

例如:使用lambda表达式将一个小写的字符串转成大写的字符串

public class DemoConsumer {
    public static void main(String[] args) {
        toUp(str->System.out.println(str.toUpperCase()),"abc");
    }

    public static void toUp(Consumer<String> consumer,String str){
        consumer.accept(str);
    }
}

6.2.2 默认方法addThen

      如果一个方法的参数和返回值全部都是Consumer类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后在做一个操作,实现组合,而这个方法就是addThen。

例如:实现将一个字符串,先转成小写,在转成大写

package com.hm.funcinter.demo;

import java.util.function.Consumer;

public class DemoConsumer {
    public static void main(String[] args) {
        toLower2up(str -> System.out.println(str.toLowerCase()),
                str->System.out.println(str.toUpperCase()),
                "hello zhongGuo!");
    }
    public static void toLower2up(Consumer<String> consumer1,Consumer<String> consumer2,String str){
        consumer1.andThen(consumer2).accept(str);
    }

}

 当然,我们也可以用两个accept方法实现,只需要在toLower2up方法中用consumer1和consumer2分别调用一次accept方法即可。

6.3 函数接口Function<T,R>接口

接收一个参数,返回另外一个类型。

java.util.function.Function<T,R>接口用于根据一个类型的数据得到另一个类型的数据,前者称之为前置条件,后者称之为后置条件,有参数有返回值。

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {
    R apply(T var1);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> var1) {
        Objects.requireNonNull(var1);
        return (var2) -> {
            return this.apply(var1.apply(var2));
        };
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> var1) {
        Objects.requireNonNull(var1);
        return (var2) -> {
            return var1.apply(this.apply(var2));
        };
    }

    static <T> Function<T, T> identity() {
        return (var0) -> {
            return var0;
        };
    }
}

function接口有一个抽象方法apply以及其他的默认方法

6.3.1 抽象方法apply 

function转换型接口,对apply方法传入的T类型的数据进行处理,返回R类型的结果,有参数有返回值。

例如:将字符串转换成整型

package com.hm.funcinter.demo;

import java.util.function.Function;

public class DemoFunction {
    public static void main(String[] args) {
        Integer num = str2Integer(str -> new Integer(str), "123");
        System.out.println(num);
    }

    public static Integer str2Integer(Function<String,Integer> fun,String str){
        return fun.apply(str);
    }
}

6.3.2 Function的链式调用andThen

Function中有一个方法andThen,可以实现链式调用。

例如:需要使用Lambda表达式将字符串转成数字,第二个操作将这个数字乘以5

package com.bjc.jdk8.function;

import java.util.function.Function;

public class Demo05Function {
    public static void main(String[] args) {
        getNumber1(str -> {return Integer.parseInt(str);},num -> {return num * 5;},"20");
    }

    public static void getNumber1(Function<String,Integer> func1,Function<Integer,Integer> func2,String str){
        Integer apply = func1.andThen(func2).apply(str);
        System.out.printf("apply = " + apply);
    }
}

6.4 BiFunction<T,R,U>

        对于Function函数式接口而言,只有一个输入参数,那如果想传两个参数呢?这时就可以用BiFunction接口,其中的Bi是Bidirectional【双向】的简写

接口定义如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T var1, U var2);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (t, u) -> {
            return after.apply(this.apply(t, u));
        };
    }
}

例如:

BiFunction<String,String,String[]> func3 = (str1,str2) -> {
    return str1.split(str2);
};
System.out.println(func3.apply("hellow","2"));

6.5 IntFunction<R>接口

定义:

@FunctionalInterface
public interface IntFunction<R> {
    R apply(int var1);
}

例如:

IntFunction<Double> func = l -> l * 2d;
Double result = func.apply(100d);
System.out.println(result);

6.6 判断型Predicate接口

        别人给一个数据,返回一个Boolean类型的数据。有时候,我们需要对某种类型的数据进行判断,从而得到一个Boolean值,这时候就可以使用java.util.function.Predicate<T>接口,该接口一般主要用于过滤一些符合条件的逻辑,除此之外还会提供三个defalut方法与一个static方法,具体请看源代码:


package java.util.function;

import java.util.Objects;

/**
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

方法说明:

修饰符和类型 函数名 函数说明
default Predicate<T> and(Predicate<? super T> other) 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND
static <T> Predicate<T> isEqual(Object targetRef) 返回一个谓词,根据equals判断两个参数是否相等
default Predicate<T> negate() 返回表示此谓词的逻辑否定的谓词
default Predicate<T> or(Predicate<? super T> other) 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或
boolean test(T t) 判断输入是否符合某个条件

 6.6.1 抽象方法test

例如:使用Lambda判断一个人的名字如果超过了3个字就认为是很长的名字

对test方法的参数T进行判断,返回Boolean类型的结果,用于条件判断场景

package com.bjc.jdk8.function;

import java.util.function.Predicate;

public class DemoPredicate {
    public static void main(String[] args) {
        Boolean name = predicateName(str -> {
            return str.length() > 3;
        }, "张三");
        System.out.println(name);
    }
    public static Boolean predicateName(Predicate<String> pre, String name){
        return pre.test(name);
    }
}

6.6.2 默认方法and

既然是条件判断接口,就会存在与、或、非三种逻辑关系,其中将两个Predicate条件使用“与”逻辑连接起来实现“并且”的效果时,就可以使用Predicate接口的default方法and,其JDK源码是:

default Predicate<T> and(Predicate<? super T> other) {
	Objects.requireNonNull(other);
	return (t) -> {
	    return this.test(t) && other.test(t);
	};
}

使用Lambda表达式判断一个字符串中既包含W,也包含H

package com.bjc.jdk8.function;

import java.util.function.Function;
import java.util.function.Predicate;

public class Demo05Function {
    public static void main(String[] args) {

        predicateTestAnd1(str -> {
            return str.contains("W");
        }, str -> {
            return str.contains("H");
        }, "ABCDXWYHZ");
        
    }

    public static Boolean predicateTestAnd1(Predicate<String> pre1, Predicate<String> pre2,String str){
        Boolean flag = pre1.and(pre2).test(str);
	if(flag){
            System.out.println("W和H都包含了。。。");
        } else {
            System.out.println("W和H没有都包含。。。");
        }
	return flag;
    }

}

6.6.3 默认方法or

使用Lambda表达式判断一个字符串中包含W或者包含H 

package com.bjc.jdk8.function;

import java.util.function.Function;
import java.util.function.Predicate;

public class Demo05Function {
    public static void main(String[] args) {

        predicateTestOr(str -> {
            return str.contains("W");
        }, str -> {
            return str.contains("H");
        }, "ABCDXWYHZ");
        
    }

    public static Boolean predicateTestAnd1(Predicate<String> pre1, Predicate<String> pre2,String str){
        Boolean flag = pre1.or(pre2).test(str);
        if(flag){
            System.out.println("W和有一个被包含了");
        } else {
            System.out.println("W和H都没有。。。");
        }
        return flag;
    }

}

6.6.4 默认方法negate

negate方法用于取反操作

例如:判断一个字符串中不包含w

package com.bjc.jdk8.function;

import java.util.function.Function;
import java.util.function.Predicate;

public class Demo05Function {
    public static void main(String[] args) {

        predicateNegate(str->{
            return str.contains("W");
        },"hello");
        
    }

    public static Boolean predicateNegate(Predicate<String> pre1,String str){
        Predicate<String> negate = pre1.negate();
        Boolean flag = negate.test(str);
        if(flag){
            System.out.println("不包含W");
        }
        return flag;
    }

}

总结:所谓的Lambda表达式,其实就是一个简写的匿名内部类,其原理就是预先生成一个匿名内部类,当执行到函数式接口的方法的时候,在调用Lambda表达式进行逻辑计算。

例如:计算一个整形数组的和

package com.bjc.jdk8.objRef;

import java.util.function.Consumer;

public class Demo1 {
    public static void main(String[] args) {
        int[] arr = {1,2,4,5,6,2,3};
        sumIntArr(a -> {
            int sum = 0;
            for (int i : a) {
                sum += i;
            }
            System.out.println(sum);
        },arr);
    }

    public static void sumIntArr(Consumer<int[]> consumer,int[] arr){
        consumer.accept(arr);
    }
}

在该例子中,main函数执行到sumIntArr函数,首先执行该函数,该函数有两个参数,一个是consumer接口,一个是int数组,程序将Lambda表达式赋给第一个参数consumer接口,将arr赋给第二个参数arr,程序调用consumer.accept就是调用Lambda表达式。然后,我们给accept传递的参数arr,就传给了Lambda的a

7. 函数式接口LongPredicate

使用test判断Long参数的用法

// 函数定义
public static List<Apple> getApplesByWeight(List<Apple> apples, LongPredicate predicate){
        List<Apple> list = new ArrayList<>();
        for(Apple a : apples){
            if(predicate.test(a.getWeight())){
                list.add(a);
            }
        }
        return list;
    }

// Lambda表达式调用
List<Apple> applesByWeight = getApplesByWeight(list, weight -> weight > 100);

8. 函数式接口 BiPredicate<T,U> 

使用test判断两个参数的用法

// 方法定义
public static List<Apple> getApplesByColorAndWeight(List<Apple> apples, BiPredicate<String,Long> predicate){
        List<Apple> list = new ArrayList<>();
        for(Apple a : apples){
            if(predicate.test(a.getColor(),a.getWeight())){
                list.add(a);
            }
        }
        return list;
    }

// 方法调用
 List<Apple> applesByColorAndWeight = getApplesByColorAndWeight(list, (c, w) -> c.equals("green") && w == 100);

9. JDK 1.8 新增加的函数接口 

java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:

接口 描述
BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果
BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
BinaryOperator<T> 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
BiPredicate<T,U> 代表了一个两个参数的boolean值方法
BooleanSupplier 代表了boolean值结果的提供方
Consumer<T> 代表了接受一个输入参数并且无返回的操作
DoubleBinaryOperator 代表了作用于两个double值操作符的操作,并且返回了一个double值的结果
DoubleConsumer 代表一个接受double值参数的操作,并且不返回结果
DoubleFunction<R> 代表接受一个double值参数的方法,并且返回结果
DoublePredicate 代表一个拥有double值参数的boolean值方法
DoubleSupplier 代表一个double值结构的提供方
DoubleToIntFunction 接受一个double类型输入,返回一个int类型结果。
DoubleToLongFunction 接受一个double类型输入,返回一个long类型结果
DoubleUnaryOperator 接受一个参数同为类型double,返回值类型也为double 。
Function<T,R> 接受一个输入参数,返回一个结果。
IntBinaryOperator 接受两个参数同为类型int,返回值类型也为int 。
IntConsumer 接受一个int类型的输入参数,无返回值 。
IntFunction<R> 接受一个int类型输入参数,返回一个结果 。
IntPredicate 接受一个int输入参数,返回一个布尔值的结果。
IntSupplier 无参数,返回一个int类型结果。
IntToDoubleFunction 接受一个int类型输入,返回一个double类型结果 。
IntToLongFunction 接受一个int类型输入,返回一个long类型结果
IntUnaryOperator 接受一个参数同为类型int,返回值类型也为int 。
LongBinaryOperator 接受两个参数同为类型long,返回值类型也为long。
LongConsumer 接受一个long类型的输入参数,无返回值。
LongFunction<R> 接受一个long类型输入参数,返回一个结果。
LongPredicate R接受一个long输入参数,返回一个布尔值类型结果。
LongSupplier 无参数,返回一个结果long类型的值。
LongToDoubleFunction 接受一个long类型输入,返回一个double类型结果。
LongToIntFunction 接受一个long类型输入,返回一个int类型结果。
LongUnaryOperator 接受一个参数同为类型long,返回值类型也为long。
ObjDoubleConsumer<T> 接受一个object类型和一个double类型的输入参数,无返回值。
ObjIntConsumer<T> 接受一个object类型和一个int类型的输入参数,无返回值。
ObjLongConsumer<T> 接受一个object类型和一个long类型的输入参数,无返回值。
Predicate<T> 接受一个输入参数,返回一个布尔值结果。
Supplier<T> 无参数,返回一个结果。
ToDoubleBiFunction<T,U> 接受两个输入参数,返回一个double类型结果
ToDoubleFunction<T> 接受一个输入参数,返回一个double类型结果
ToIntBiFunction<T,U> 接受两个输入参数,返回一个int类型结果。
ToIntFunction<T> 接受一个输入参数,返回一个int类型结果
ToLongBiFunction<T,U> 接受两个输入参数,返回一个long类型结果。
ToLongFunction<T> 接受一个输入参数,返回一个long类型结果。
UnaryOperator<T> 接受一个参数为类型T,返回值类型也为T。
发布了205 篇原创文章 · 获赞 9 · 访问量 7921

猜你喜欢

转载自blog.csdn.net/weixin_43318134/article/details/104190274
今日推荐