JAVA函数式编程之函数式接口

本文介绍了Java8中的函数式编程特性,包括Lambda表达式的类型推导、方法引用的使用,以及@FunctionalInterface注解的作用。通过案例展示了如何使用函数式接口进行方法转换,并探讨了高阶函数的概念。此外,还讨论了如何使用函数式接口替代反射来提高代码的可读性和效率。
摘要由CSDN通过智能技术生成

        前言:

       方法引用和Lambda表达式必须被赋值,同时编译器需要识别类型信息以确保类型正确。但是你怎么知道传递给方法的参数的类型?

       案例1:

       x->x.toString()我们清楚这里返回类型必须是String,但x是什么类型呢?Lambda表达式包含类型推导(编译器会自动推导出类型信息,避免了程序员显式地声明)。编译器必须能够以某种方式推导出x的类型。

       案例2:
       (x,y)->x+y现在x和y可以是任何支持+运算符连接的数据类型,可以是两个不同的数值类型或者是1个String加任意一种可自动转换为String的数据类型(这包括了大多数类型)。但是,当Lambda表达式被赋值时,编译器必须确定x和y的确切类型以生成正确的代码。

       为了解决这个问题,Java8引入了java.util.function包。它包含一组接口,这些接口是Lambda表达式和方法引用的目标类型。每个接口只包含一个抽象方法,称为函数式方法。在编写接口时,可以使用@FunctionalInterface注解强制执行此“函数式方法”

 

 一、简单应用

@FunctionalInterface
public interface Functional {
    String goodbye(String arg);
}


public interface FunctionalNoAnn {
    String goodbye(String arg);
}


@FunctionalInterface
public interface NotFunctional {
  //@FunctionalInterface的值在NotFunctional的,定义中可见:接口中如果有多个方法则会产生编译时错误消息
  //String goodbye(String arg);
  String hello(String arg);
}

public class FunctionalAnnotation {
    public String goodbye(String arg){
        System.out.println("Goodbye," + arg);
        return "";
    }
    public static void main(String[] args){
        FunctionalAnnotation fa = new FunctionalAnnotation();
        Functional f = fa::goodbye;
        FunctionalNoAnn fna = fa::goodbye;
//        Functional fac = fa; //Incompatible
        Functional fl = a -> "Goodbye," + a ;
        FunctionalNoAnn fnal = a->"Goodbye," + a;

        System.out.println(f.goodbye("FunctionalAnnotation"));
        System.out.println(fna.goodbye("FunctionalAnnotation"));
        System.out.println(fl.goodbye("Functional"));
        System.out.println(fnal.goodbye("FunctionalNoAnn"));
    }
}

执行结果
---------------------------------------------------------------------------


Goodbye,FunctionalAnnotation

Goodbye,FunctionalAnnotation

Goodbye,Functional
Goodbye,FunctionalNoAnn

  • @FunctionalInterface注解是可选的;
  • Java在main()中把Functional和Func-tionalNoAnn都当作函数式接口
  • @FunctionalInterface的值在NotFunctional的定义中可见:接口中如果有多个方法则会产生编译时错误消息。
  • 观察一下f和fna两个变量调用的是类中的方法,而并不是自己接口中的方法,但是我们的FunctionalAnnotation类并没有实现两个函数是接口,。Java8新特性:如果将方法引用或Lambda表达式赋值给函数式接口(类型需要匹配),Java会适配你的赋值到目标接口。编译器会自动包装方法引用或Lambda表达式到实现目标接口的类的实例中(大白话就是,引用匹配方法至关注的是参数类表与返回类型,不关注继承实现引)。
  • Functional fac = fa; 会编译错误,因为它没有明确地实现Functional接口。但是,Java8允许我们以简便的语法为接口赋值函数。java.util.function包创建一组完整的目标接口,使得我们一般情况下不需再定义自己的接口。

二、使用函数接口时,名称无关紧要

      

class In1{}

class In2{}

public class MethodConversion {
    static void accept(In1 i1,In2 i2){
        System.out.println("accept()");
    }
    static void someOtherName(In1 i1,In2 i2){
        System.out.println("someOtherName()");
    }
    public static void main(String[]args){
        BiConsumer<In1,In2> bic;
        bic=MethodConversion::accept;
        bic.accept(new In1(),new In2());
        bic=MethodConversion::someOtherName;
       //bic.someOtherName(newIn1(),newIn2());//Nope
        bic.accept(new In1(),new In2());
    }
}
-----------------------------------------------------------------

accept()
someOtherName()
  • 观察输出结果:在使用函数接口时,名称无关紧要——只要参数类型和返回类型相同。Java会将你的方法映射到接口方法。

 

三、高阶函数

高阶函数(Higher-orderFunction)只是一个消费或产生函数的函数。

public interface FuncSS extends Function<String,String> {

}


public class ProduceFunction {
    static FuncSS produce(){
        return s->s.toLowerCase();//[2]
    }
    public static void main(String[] args){
        FuncSS f = produce();
        System.out.println(f.apply("YELLING"));
    }
}

--------------------------------------------------------------

yelling
  • 这里,produce()是高阶函数。

四、andThen()使用

class Apple{
    @Override
    public String toString(){
        return"Apple";
    }
}
class Banana{
    @Override
    public String toString(){
        return"Banana";
    }
 }

public class TransformFunction {
    static Function<Apple,Banana> transform(Function<Apple,Banana> in){
        return in.andThen(o->{
            System.out.println(o);
            return o;
        });
    }
    public static void main(String[] args){
        Function<Apple,Banana> f2 = transform(i->{
            System.out.println(i);
            return new Banana();
        });
        Banana banana = f2.apply(new Apple());
    }
}

        这里使用到了Function接口中名为andThen()的默认方法,该方法专门用于操作函数。顾名思义,在调用in函数之后调用toThen()(还有个compose()方法,它在in函数之前应用新函数)。要附加一个andThen()函数,我们只需将该函数作为参数传递。transform()产生的是一个新函数,它将in的动作与andThen()参数的动作结合起来。

 

五、函数式接口替代反射

       现实开发中我阅读过好多代码满篇if() else if()...... else(),最长的见过else if() 超越50个,这种代码初入职场写写也就ok,但是真的遇到过5年以上的程序员还是这么写,技术好些的会在业务上分析,或是用反射,但是大家都知道反射的性能并不是很好,如果在数据量较少,性能要求不高的情况下可以使用,java8之后我们好的场景下可以用函数式接口替代反射。

   

public class Function {
    private String var1;
    private String var2;
    private String var3;
    private String var4;
    private String var5;
    private String var6;
    private String var7;
    private String var8;
    private String var9;
    private String var10;

    public String getVar1() {
        return var1;
    }

    public void setVar1(String var1) {
        this.var1 = var1;
    }

    public String getVar2() {
        return var2;
    }

    public void setVar2(String var2) {
        this.var2 = var2;
    }

    public String getVar3() {
        return var3;
    }

    public void setVar3(String var3) {
        this.var3 = var3;
    }

    public String getVar4() {
        return var4;
    }

    public void setVar4(String var4) {
        this.var4 = var4;
    }

    public String getVar5() {
        return var5;
    }

    public void setVar5(String var5) {
        this.var5 = var5;
    }

    public String getVar6() {
        return var6;
    }

    public void setVar6(String var6) {
        this.var6 = var6;
    }

    public String getVar7() {
        return var7;
    }

    public void setVar7(String var7) {
        this.var7 = var7;
    }

    public String getVar8() {
        return var8;
    }

    public void setVar8(String var8) {
        this.var8 = var8;
    }

    public String getVar9() {
        return var9;
    }

    public void setVar9(String var9) {
        this.var9 = var9;
    }

    public String getVar10() {
        return var10;
    }

    public void setVar10(String var10) {
        this.var10 = var10;
    }
}

 

public class ReflectFunction {
    static String[] arr = { "fun01", "fun02", "fun03", "fun04", "fun05", "fun06", "fun07", "fun08", "fun09", "fun10" };

    static List<BiConsumer<Function, String>> biConsumers = Arrays.asList(Function::setVar1, Function::setVar2, Function::setVar3,
            Function::setVar4, Function::setVar5, Function::setVar6, Function::setVar7, Function::setVar8, Function::setVar9, Function::setVar10);

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        IntStream.range(0, 100000).forEach(i -> testReflect());
        long end = System.currentTimeMillis();
        System.out.println(end - start);

        long start1 = System.currentTimeMillis();
        IntStream.range(0, 100000).forEach(i -> testMethodReference());
        long end1 = System.currentTimeMillis();

        System.out.println(end1 - start1);
    }

    static void testReflect() {
        Function function = new Function();

        try {
            Class<?> clazz = Class.forName("test.function.Function");
            for (int i = 0; i < arr.length - 1; i++) {
                Method method = clazz.getMethod("setVar" + (i + 1), String.class);
                method.invoke(function, arr[0]);
            }

        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException
                | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    static void testMethodReference() {
        Function function = new Function();

        for (int i = 0; i < arr.length; i++) {
            biConsumers.get(i).accept(function, arr[i]);
        }
    }
}

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值