【Java】函数式接口

函数式接口

转载请标注出处: 函数式接口

函数式接口 ( Functional Interface ) 是一个包含一个抽象方法接口

下面通过一个例子来感受一下函数式接口。

@FunctionalInterface
public interface TestFunctionInterface {
    void test();
}

public class Main {
    public static void main(String[] args) {
        TestFunctionInterface t=()->System.out.println("hello");
        t.test();
    }
}

这个例子中,我们定义了一个 TestFunctionInterface 的接口,这个接口有一个 test 方法。

我们在 main 里面将一个 Lambda 表达式作为一个接口的实现复制给这个接口。

再谈谈 @FunctionalInterface 注解,此注解用于标明一个接口是函数式接口。

我们可以在任意的函数式接口上使用 @FunctionalInterface 注解,这样做可以让编译器检查它所注解的接口是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

补充一点,若Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明。下面给出一个接受抛出 IOException 异常的 Lambda 表达式的函数式接口定义。

@FunctionalInterface
public interface TestFunctionInterface {
    void test() throws IOException;
}

通过以上例子,我们已经明白了一个自定义的函数式接口应该如何定义,但是我们在实际开发中,并不需要完全靠我们自己去定义函数式接口,而是使用 Java 内置的函数式接口。下面介绍四个核心的函数式接口。

函数式接口参数类型返回类型用途
Consumer 消费型接口Tvoid对类型为 T 的对象应用操作,包含方法:void accept(T t);
Supplier 供给型接口T返回类型为T的对象,包含方法:T get();
Function<T,R> 函数型接口TR对类型为 T 的对象应用操作,并返回结果。结果是 R 类型的对象。包含方法:R apply(T t);
Predicate 断定型接口Tboolean确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法 boolean test(T t);

通过浏览上面的表格,我们很容易发现,内置的函数式接口都是泛型与函数式接口结合的。

下面我们来使用一下内置的 Comsumer 函数式接口。

public class Main {
    public static void main(String[] args) {
        Consumer<String> handler=str->{
            int n=str.length()/2;
            String s1=str.substring(0,n).toUpperCase();
            System.out.println(s1+str.substring(n).toLowerCase());
        };
        handler.accept("hello world!");
    }
}

这个例子,我们定义了一个对字符串前面一半的字母转为大写,后半部分转为小写的一个 Consumer 接口。使用 accept 方法执行操作,我们发现 Consumer 还有一个方法是 andThen 方法,此方法需要传入一个 Consumer 对象,这样使得本 Consumer 对象的操作可以先执行,然后再执行传入的 Consumer 的操作,因此我们可以写出以下代码。

handler.andThen((str)->System.out.println("second ")).accept("first");

这样,first 将先于 second 输出。

同样的,我们也能对于其余的 Supplier, Function, Predicate 接口进行测试。接下来我们会通过使用 Predicate 接口在方法设计的一个案例来体验函数式接口的优势。

class Item {
    boolean visibility;
    // ...
    public void show(){
        // 绘制自己
        // ...
    }
    public boolean getVisibility(){
        return visibility;
    }
}
class Widget {
    List<Item> items;

    public List<Item> getItem() {
        return items;
    }
    // ...
}
public class Main {
    public void showWidget(Widget w,Predicate<Item> v){
        for(Item i:w.getItem()){
            if(v.test(i)){
                i.show();
            }
        }
    }
    
    public static void main(String[] args) {
        Main m=new Main();
        Widget w=new Widget();
        m.showWidget(w,item -> item.getVisibility());
    }
}

首先看 Main 类里面,我先定义了一个 Main 的对象,这里创建这个对象是因为 main 方法是静态方法,不能直接调用非静态方法。这里假设了一个场景,我们需要在 Main 类中去显示窗口,而一个窗口里面的元素是否需要显示,则根据自己的条件去判断是否需要显示。

showWidget 方法中,我们使用函数式接口作为参数,这使得我们可以将代码像数据一样传递。

Item 类中的 visibility 字段决定了是否需要显示,但是实际上,如果我们有类似的场景时候,我们可以通过 Lambda 表达式灵活改变显示的条件,而无需改变原有的 showWidget 代码。

这使得我们代码的弹性更好,能够更加轻易的应对变化。

总结一下,我们通过使用在函数式接口中使用泛型大大增强了函数式接口的能力,在实际运用中,函数式接口往往作为方法的参数来使用,将一些未确定的代码与现在的代码隔离开来。Lambda 表达式往往用于实例化函数接口,因此两者往往配合使用。

附录

JDK 1.8 之前已有的函数式接口:

  • java.lang.Runnable

  • java.util.concurrent.Callable

  • java.security.PrivilegedAction

  • java.util.Comparator

  • java.io.FileFilter

  • java.nio.file.PathMatcher

  • java.lang.reflect.InvocationHandler

  • java.beans.PropertyChangeListener

  • java.awt.event.ActionListener

  • javax.swing.event.ChangeListener

JDK 1.8 新增加的函数接口都在 java.util.function 包中,下表列举了该包中的函数式接口。

序号接口 & 描述
1BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果
2BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
3BinaryOperator< T > 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
4BiPredicate<T,U> 代表了一个两个参数的boolean值方法
5BooleanSupplier 代表了boolean值结果的提供方
6Consumer< T > 代表了接受一个输入参数并且无返回的操作
7DoubleBinaryOperator 代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
8DoubleConsumer 代表一个接受double值参数的操作,并且不返回结果。
9DoubleFunction 代表接受一个double值参数的方法,并且返回结果
10DoublePredicate 代表一个拥有double值参数的boolean值方法
11DoubleSupplier 代表一个double值结构的提供方
12DoubleToIntFunction 接受一个double类型输入,返回一个int类型结果。
13DoubleToLongFunction 接受一个double类型输入,返回一个long类型结果
14DoubleUnaryOperator 接受一个参数同为类型double,返回值类型也为double 。
15Function<T,R> 接受一个输入参数,返回一个结果。
16IntBinaryOperator 接受两个参数同为类型int,返回值类型也为int 。
17IntConsumer 接受一个int类型的输入参数,无返回值 。
18IntFunction 接受一个int类型输入参数,返回一个结果 。
19IntPredicate 接受一个int输入参数,返回一个布尔值的结果。
20IntSupplier 无参数,返回一个int类型结果。
21IntToDoubleFunction 接受一个int类型输入,返回一个double类型结果 。
22IntToLongFunction 接受一个int类型输入,返回一个long类型结果。
23IntUnaryOperator 接受一个参数同为类型int,返回值类型也为int 。
24LongBinaryOperator 接受两个参数同为类型long,返回值类型也为long。
25**LongConsumer **接受一个long类型的输入参数,无返回值。
26LongFunction< R > 接受一个long类型输入参数,返回一个结果。
27LongPredicate R接受一个long输入参数,返回一个布尔值类型结果。
28**LongSupplier **无参数,返回一个结果long类型的值。
29**LongToDoubleFunction **接受一个long类型输入,返回一个double类型结果。
30LongToIntFunction 接受一个long类型输入,返回一个int类型结果。
31**LongUnaryOperator **接受一个参数同为类型long,返回值类型也为long。
32**ObjDoubleConsumer< T > **接受一个object类型和一个double类型的输入参数,无返回值。
33ObjIntConsumer< T > 接受一个object类型和一个int类型的输入参数,无返回值。
34**ObjLongConsumer< T > **接受一个object类型和一个long类型的输入参数,无返回值。
35**Predicate< T > **接受一个输入参数,返回一个布尔值结果。
36Supplier< T > 无参数,返回一个结果。
37**ToDoubleBiFunction<T,U> **接受两个输入参数,返回一个double类型结果
38**ToDoubleFunction< T > **接受一个输入参数,返回一个double类型结果
39**ToIntBiFunction<T,U> **接受两个输入参数,返回一个int类型结果。
40ToIntFunction< T > 接受一个输入参数,返回一个int类型结果。
41ToLongBiFunction<T,U> 接受两个输入参数,返回一个long类型结果。
42ToLongFunction< T > 接受一个输入参数,返回一个long类型结果。
43**UnaryOperator< T > ** 接受一个参数为类型T,返回值类型也为T。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值