函数式接口

一、什么是函数式接口

(1)函数式接口定义

如果一个接口只有一个抽象方法,则该接口称之为函数式接口。

函数式接口可以使用Lambda表达式,lambda表达式会被匹配到这个抽象方法上 。通过Lambda表达式来创建该接口的对象,若Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明。

为了确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。

(2)自定义函数式接口及使用

示例一:

package functionInterface;

import java.util.function.Consumer;

public class StuFuncInterface {
    public static void main(String[] args) {
        //内部类
        MyFunc myFunc = new MyFunc() {
            @Override
            public void sayHello() {
                System.out.println("Hello World!");
            }
        };
        //Lambda表达式
        MyFunc myFunc1 = ()-> System.out.println("Hello World");
    }
}

@FunctionalInterface
interface MyFunc{
    public void sayHello();
}

示例二:使用泛型

泛型表示方法参数类型

package functionInterface;

import java.util.function.Consumer;

public class StuFuncInterface {
    public static void main(String[] args) {
        //内部类
        MyFunc<String> myFunc = new MyFunc<String>() {
            @Override
            public void sayHello(String name) {
                System.out.println("Hello,"+name);
            }
        };
        //Lambda表达式
        MyFunc<String> myFunc1 = (String name)-> System.out.println("Hello,"+name);
    }
}

@FunctionalInterface
interface MyFunc<T>{
    public void sayHello(T t);
}

示例三:带返回值 

package functionInterface;

import java.util.function.Consumer;

public class StuFuncInterface {
    public static void main(String[] args) {
        //内部类
        Converter<Integer,String> converter = new Converter<Integer, String>() {
            @Override
            public Integer converter(String number) {
                return new Integer(number);
            }
        };
        //Lambda表达式
        Converter<Integer,String> converter1 = (String number)->{
            return new Integer(number);
        };
        //省略形参类型和return
        Converter<Integer,String> converter2 = (number)->new Integer(number);
    }
}

@FunctionalInterface
interface Converter<T,F>{
    T converter(F f);
}

二、Java内置的四大核心函数式接口

Java为了程序员方便使用Lambda表达式,Java内置了四个核心函数式接口  。

函数式接口

参数类型

返回类型

说明

Consumer<T> 消费型接口

T

void

void accept(T t):对类型为T的对象应用操作

Supplier<T> 供给型接口


T

T get():返回类型为T的对象

Function<T,R> 函数型接口

T

R

R apply(T t):对类型为T的对象应用操作,并返回类型为R类型的对象。

Predicate<T> 断言型接口

T

boolean

boolean test(T t):确定类型为T的对象是否满足条件,并返回boolean类型。

(1)消费型接口 :Consumer<T>

方法有参数无返回值。

package functionInterface;

import java.util.function.Consumer;

public class StuFuncInterface {
    public static void main(String[] args) {
        //内部类
        happy(8000, new Consumer<Double>() {
            @Override
            public void accept(Double money) {
                System.out.println("华为鸿蒙旗舰机"+money);
            }
        });
        //Lambda表达式
        happy(1000,(money)-> System.out.println("聚餐消费"+money));
        //只有一个参数时,小括号可以省略
        happy(1000,money-> System.out.println("聚餐消费"+money));
}

    public static void happy(double money, Consumer<Double> consumer){
        consumer.accept(money);
    }
}

(2)供给型接口:Supplier<T>

方法无参有返回值。

package functionInterface;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;

public class StuSupplier {
    public static void main(String[] args) {
        //获取5个0~50的随机数
        //内部类
        List<Integer> numbers1 = getNumbers(5, new Supplier<Integer>() {
            @Override
            public Integer get() {
                return new Random().nextInt(50);
            }
        });
        //遍历
        for (Integer n : numbers1) {
            System.out.println(n);
        }
        System.out.println("----------------------定海神针-----------------");
        //Lambda表达式
        List<Integer> numbers2 = getNumbers(5, ()->new Random().nextInt(50));
        //Stream API 遍历,方法引用输出
        numbers2.stream().forEach(System.out::println);
    }

    public static List<Integer> getNumbers(int count, Supplier<Integer> supplier) {
        List<Integer> numbers=new ArrayList<>();
        for(int i=0;i<count;i++) {
            numbers.add(supplier.get());
        }
        return numbers;
    }
}

Stream API 和 方法引用后面文章会讲,这里先看一下,混个脸熟。

(3)函数型接口:Function<T,R>

方法有参数:T,且有返回值:R

package functionInterface;

import java.util.function.Function;

public class StuFunction {
    public static void main(String[] args) {
        //内部类
        System.out.println(strOperation("hello", new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        }));
        //Lambda表达式
        System.out.println(strOperation("   hello    ", (s)->s.trim()));
        System.out.println(strOperation("hello",s -> s.toUpperCase()));
    }

    public static String strOperation(String s, Function<String, String> fun) {
        return fun.apply(s);
    }

}

(4)断言型接口:Predicate<T>

方法有参数有返回值,但是返回值必须是boolean类型。

我们在Lambda的策略模式时,就自己定义了一个断言型的接口,然后使用,其实这里的Predicate接口和我们写的是一样的。

Predicate接口源码:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);
    //省略其他默认方法和静态方法
}

断言型接口主要用来测试一个参数是否满足指定条件,满足返回true,否则返回false。

示例:

package functionInterface;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class StuPredicate {
    public static void main(String[] args) {
        List<String> names=new ArrayList<>();
        names.add("张三");
        names.add("张三丰");
        names.add("张无忌");
        names.add("李四");
        names.add("王五");
        //挑选出字符串中有“张”字的
        //内部类
        List<String> names1 = filterStr(names, new Predicate<String>() {
            @Override
            public boolean test(String name) {
                return name.startsWith("张");
            }
        });
        for (String string : names1) {
            System.out.println(string);
        }
        System.out.println("---------------------定海神针-------------------");
        //Lambda表达式
        List<String> names2 = filterStr(names,name->name.startsWith("张"));
        names2.stream().forEach(System.out::println);
    }

    //使用Predicate,过滤字符串
    public static List<String> filterStr(List<String> list, Predicate<String> predicate) {
        List<String> list2=new ArrayList<>();
        for (String string : list) {
            if(predicate.test(string)) {
                list2.add(string);
            }
        }
        return list2;
    }
}

好了,Java内置的四大核心函数式接口的简单使用就介绍完了。上面例子中使用到了一个新的符号 “::” ,在Lambda表达式中Java引入了 “->” 符号;而 “::” 符号是Java 8 另一个新特性——方法引用中引入的新符号,下篇博客我们详细介绍Java 8 的有一个新特性——方法引用。