Java函数式接口看这一篇就够了

目录:
1.函数式接口的基本概念和格式
2.函数式编程
3.函数式接口作为方法的参数和方法的返回值
4.常用函数式接口
在这里插入图片描述

1.函数式接口的基本概念和格式

1.函数式接口的基本概念:

函数式接口在Java中是指:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可 以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

如果对Lambda表达式不是很清除那么可以参照这一篇博客:Lambda表达式详解

介绍一个语法糖概念:

语法糖:是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实 底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部 类的“语法糖”,但是二者在原理上是不同的。

2.格式:

只要满足有一个抽象方法的接口即可,当然也可以包含其他的方法(默认静态私有等等)

修饰符 interface 接口名称
 { 
 (public abstract) 返回值类型 方法名称(可选参数信息); 
 // 其他非抽象方法内容 
}

由于接口中默认类型为public abstract 所以带不带都行

3.@FunctionalInterface注解:

Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface。该注 解可用于一个接口的定义上

扫描二维码关注公众号,回复: 11271799 查看本文章
@FunctionalInterface
 public interface MyFunctionalInterface
  { void myMethod(); }

我们这一个注解当我们在定义函数式接口的时候发送了错误,那么注解的编译就不通过:
在这里插入图片描述当我们去掉一个抽象的方法的时候就,注意就会正常通过编译:
在这里插入图片描述

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注 意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

2.函数式编程

1.Lambda表达式的延迟执行
看一段代码:

  public class Demo01Logger { 
  private static void log(int level, String msg) { 
  if (level == 1) 
   { 
       System.out.println(msg); 
   } 
  }
  public static void main(String[] args) 
  { 
   String msgA = "Hello";
   String msgB = "World"; 
   String msgC = "Java"; 
   log(1, msgA + msgB + msgC); 
   }
}

分析一下这段代码,发现存在浪费,首先log方法传进两个参数,第一个参数是一个数字,第二个参数是一个拼接后的字符串,那么正常调用的时候我们就要先把字符串拼接好后再调用log函数,如果第一个参数不是1,那么拼接字符串就显得没用,白拼接了,这样就造成了浪费,这时候就可以使用Lambda解决

想要解决问题,那么首先我们了解Lambda的使用特点和前提:

1.使用特点:延迟加载
2.使用前提:必须存在函数式接口

具体解决的代码实现:

package untl1;
public class Demo02LoggerLambda
{
    private static void log(int level, MessageBuilder builder)
    {
        if (level == 1)
        {
            System.out.println(builder.buildMessage());
        }
    }
    public static void main(String[] args)
    {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
        log(1, ()-> msgA+msgB+msgC);
    }
}
@FunctionalInterface
interface MessageBuilder {
    String buildMessage();
}

此段代码就完美的解决上述问题,那么,我们如何证实在第一个参数不是1的时候不进行字符串的拼接呢,我们可以在第二个参数加一个书输出语句即

package untl1;
public class Demo02LoggerLambda
{
    private static void log(int level, MessageBuilder builder)
    {
        if (level == 1)
        {
            System.out.println(builder.buildMessage());
        }
    }
    public static void main(String[] args)
    {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
        log(1, ()-> {
            System.out.println("我第一个参数是1");
            return msgA+msgB+msgC+1;});
        log(2, ()-> {
            System.out.println("我第一个参数不是1");
            return msgA+msgB+msgC+2;});
    }
}
@FunctionalInterface
interface MessageBuilder {
    String buildMessage();
}
运行结果:
我第一个参数是1
HelloWorldJava1

3.函数式接口作为方法的参数和方法的返回值

作为方法的参数,上边修正过的例子就是,作为方法的返回值如下例:

package untl1;
public class Demo02LoggerLambda
{
    private static MessageBuilder log()
    {
      return (a,b)->{
       return a+b;
      };

    }
    public static void main(String[] args)
    {
      MessageBuilder a=log();
        System.out.println(a.buildMessage("hello","world"));
    }
}
@FunctionalInterface
interface MessageBuilder {
    String buildMessage(String a,String b);
}
运行结果:
helloworld

注意return中的a和b是两个变量名称,可以随意更改

4.常用函数式接口

1.supplier接口:
(1) 简介:

java.util.function.Supplier<T> 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对 象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象 数据,supplier接口又被称为生产型接口,指定接口是什么类型,那么接口的get方法就会产生一个什么类型的数据类型

(2)例子:

package untl1;
import java.util.function.Supplier;
public class MySupplier {
    public static  String getString(Supplier<String> sup)
    {
        return sup.get();
    }

    public static void main(String[] args) {
        String str=getString(()->"helloworld");
        System.out.println(str);
    }

}
运行结果:
helloworld

2.Consumer接口:
(1)简介:

java.util.function.Consumer<T> 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。,至于具体怎么消费如何消费需要自定义(输出或者计算等等…)

(2)l例子:

package untl1;
import java.util.function.Consumer;
public class MyConsumer {
    public static void method(String name, Consumer<String> con)
    {
        con.accept(name);
    }

    public static void main(String[] args) {
        method("我是帅哥",(String name1)-> System.out.println(name1));
    }
}

(3).Consumer默认方法andThen

如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代码:

default Consumer<T> andThen(Consumer<? super T> after) 
{ 
  Objects.requireNonNull(after);
  return (T t)> {
  accept(t); 
  after.accept(t); 
  };
}

其实andThen方法简单来说就是把两个Consumer接口组合在一起,在分别对数据进行消费
下面我们不使用andThen和使用andThen的两个例子做对比:
例子1(不使用andThen):

package untl1;
import java.util.function.Consumer;
public class MyConsumer {
  public static void method(String str, Consumer<String> a,Consumer<String> b)
  {
      a.accept(str);
      b.accept(str);
  }

    public static void main(String[] args) {
        method("hello",
                (String a)-> System.out.println(a),
                (String b)-> System.out.println(b.toUpperCase()));
    }

}
运行结果:
hello
HELLO

我们使用andThen之后:

package untl1;
import java.util.function.Consumer;
public class MyConsumer {
  public static void method(String str, Consumer<String> a,Consumer<String> b)
  {
    a.andThen(b).accept(str);
  }

    public static void main(String[] args) {
        method("hello",
                (String a)-> System.out.println(a),
                (String b)-> System.out.println(b.toUpperCase()));
    }

}
运行结果和上边的一样

上述代码由于是a连接b那么先执行a消费数据,然后再执行消费数据
3.Predicate接口:
(1)简介;

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate<T>接口。Predicate 接口中包含一个抽象方法: boolean test(T t) 。

(2)例子:

package untl1;
import java.util.function.Predicate;
public class MyPredicate {
    public static boolean checkString(String str, Predicate<String> pre)
    {
        return pre.test(str);
    }
    public static void main(String[] args) {
        String str="abc";
        boolean b=checkString(str,a->a.length()>6);
        System.out.println(b);
    }
}
运行结果:
false

(3)默认方法and:

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

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

例子:
我么判断一个字符串里边长度是不是为5,里边是不是包含a这个字符:

package untl1;
import java.util.function.Predicate;
public class MyPredicate {
    public static boolean checkString(String str, Predicate<String> a,Predicate<String> b)
    {
        return a.and(b).test(str);//其实就相当于return a.test(str)&&b.test(str)
    }
    public static void main(String[] args) {
       boolean a= checkString("abc",str->str.length()==5,str->str.contains("a") );
        System.out.println(a);
    }
}
运算结果:
false

(4)默认方法or:

与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。

例子:

package untl1;
import java.util.function.Predicate;
public class MyPredicate {
    public static boolean checkString(String str, Predicate<String> a,Predicate<String> b)
    {
        return a.or(b).test(str);//其实就相当于return a.test(str)||b.test(str)
    }
    public static void main(String[] args) {
       boolean a= checkString("abc",str->str.length()==5,str->str.contains("a") );
        System.out.println(a);
    }
}
运行结果:
true

(5)默认方法negate:

“或”已经了解了,剩下的“非”(取反)也会简单。

例子:

package untl1;
import java.util.function.Predicate;
public class MyPredicate {
    public static boolean checkString(String str, Predicate<String> a)
    {
        return a.negate().test(str);
    }
    public static void main(String[] args) {
       boolean a= checkString("abc",str->str.length()==5);
        System.out.println(a);
    }
}
运行结果:
true

5.Function接口:
(1)简介

java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。Function 接口中最主要的抽象方法为:R apply(T t) ,根据类型T的参数获取类型R的结果。 使用的场景例如:将 String 类型转换为 Integer 类型。

(2)
例子:

package untl1;
import java.util.function.Function;
public class MyFunction {
    public static void change(String str, Function<String,Integer> fun)
    {
        int  in=fun.apply(str);//自动拆箱
        System.out.println(in+1);
    }
    public static void main(String[] args) {
        String str="1234";
        change(str,a-> Integer.parseInt(a));
    }

}
运行结果:
1235

(3)默认方法andThen:

同样进行组合操作:

例子:
我们把String类型的123转换成Integer加上10,再转换成String类型:

package untl1;
import java.util.function.Function;
public class MyFunction {
    public static void change(String str, Function<String,Integer>       
    fun1,Function<Integer,String>  fun2)
    {
        String str1=fun1.andThen(fun2).apply(str);
        System.out.println(str1);
    }
    public static void main(String[] args) {
        String str="123";
        change(str,a-> Integer.parseInt(a)+10,b->b+"");
    }

}
运行结果:
133

猜你喜欢

转载自blog.csdn.net/qq_45737068/article/details/105734234