每一秒钟的时间都值得铭记

0%

JDK8新特性之函数式接口

函数式接口

  • JDK8更新了很多的新特性,其中函数式接口就是JDK8中最为受欢迎的新特性之一。

什么是函数式接口?

  • 有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

函数式接口是适用于函数式编程的接口,而JDK8中的函数式编程就是Lambda,所以函数式接口就是可以使用于Lambda表达式使用的接口,只有确保接口中只有一个抽象方法,Java中的Lambda才可以顺利进行推导。

格式

1
2
3
public interface MyFunctionInterface {
void method();
}

@FunctionalInterface注解

@FunctionalInterface注解是一个和函数式接口相关的注解,可以检测一个接口是否是一个函数式接口。如果是则编译成功,如果不是则编译失败。
编译失败的可能原因:接口中没有抽象方法或者有多个抽象方法。

编译成功的情况:

1
2
3
4
@FunctionalInterface		//编译成功
public interface MyFunctionInterface {
void method();
}

编译失败的情况:

1
2
3
4
5
@FunctionalInterface		//编译失败
public interface MyFunctionInterface {
void method();
void method2();
}

Lambda表达式

Lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体只包含了一行代码,可以不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda表达式的使用

我们平常面向接口编程的时候,都需要实现该接口,重写内部方法,我们可以使用匿名内部类的方式编写代码,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public static void show(MyFunctionInterface myInterface) {
myInterface.method();
}

public static void main(String[] args) {
show(new MyFunctionInterface() {
@Override
public void method() {
System.out.println("使用匿名内部类的方式重写接口中的方法。");
}
});
}

而如果这个接口是一个函数式接口,那么我们可以使用Lambda表达式来重写这个接口中的方法,简化代码。

1
2
3
4
5
6
7
8
9
public static void show(MyFunctionInterface myInterface) {
myInterface.method();
}

public static void main(String[] args) {
show(()->{
System.out.println("使用Lambda表达式的方式重写接口中的方法。");
});
}

Lambda表达式的简化:如果这个重写的方法中有且仅有一行代码,那么我们可以省略方法体中的大括号和分号。

1
2
3
4
5
6
7
public static void show(MyFunctionInterface myInterface) {
myInterface.method();
}

public static void main(String[] args) {
show(()->System.out.println("使用Lambda表达式的方式重写接口中的方法。"));
}

匿名内部类和Lambda表达式的区别?

匿名内部类会生成一个匿名内部类的class文件,而Lambda表达式不会有class文件,节省了空间。

Lambda表达式的延迟执行

有些场景的代码执行后,结果有不一定会被使用,从而造成了性能的浪费,而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。

性能浪费的日志案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Logger {
public static void showLog(int level, String message) {
if(level == 1) {
System.out.println(message);
}
}

public static void main(String[] args) {
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
showLog(1, msg1+msg2+msg3);
}
}

以上代码中,如果传入的level参数为2,那么message参数传入的拼接字符串就造成了性能浪费。

使用Lambda表达式的延迟执行优化这段代码

1
2
3
4
@FunctionalInterface
public interface MessageBuilder {
String builderMessage();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void showLog(int level, MessageBuilder mb) {
if(level == 1) {
System.out.println(mb.builderMessage());
}
}

public static void main(String[] args) {
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
showLog(1, ()->{
System.out.println("当条件符合时才会执行这段方法!");
return msg1+msg2+msg3;
});
}

使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中。

  • 只有满足条件,参数level为1时,才会调用的MessageBuilder接口中的builderMessage方法,字符串才会进行拼接。
  • 如果不满足条件,参数level不为1时,不会调用的MessageBuilder接口中的builderMessage方法,字符串也不会进行拼接。

这就是Lambda表达式的延迟执行,避免了性能的浪费。

Lambda表达式和线程

线程中的Runnable接口就是一个函数式接口,其中有且仅有一个抽象方法run()方法。

我们就可以使用Lambda表达式来创建线程。

1
2
3
4
5
public static void main(String[] args) {
new Thread(()->
System.out.println(Thread.currentThread().getName()+"线程启动了!")
).start();
}

常用的函数式接口

  • JDK为我们提供了一些常用的函数式接口,这些接口全部放在java.util.function包中。

Supplier接口

Supplier是一个函数式接口,其中仅有一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。

  • Supplier接口被称之为生产型接口,指定接口是什么泛型,就会生产什么类型的数据。
1
2
3
4
5
6
7
8
9
10
public class SupplierDemo {
public static String getString(Supplier<String> supplier) {
return supplier.get();
}

public static void main(String[] args) {
String str = getString(()->"Supplier接口生产一个String类型的数据。");
System.out.println(str);
}
}

Consumer接口

  • Consumer接口被称之为消费型接口,指定什么类型的泛型,就会消费什么类型的数据。

抽象方法:void accept(T t)

1
2
3
4
5
6
7
8
9
10
11
12
public class ConsumerDemo {
public static void method(String name, Consumer<String> consumer) {
consumer.accept(name);
}

public static void main(String[] args) {
method("消费了这个字符串", (t)->{
//消费方法的逻辑可以由设计者随便定义
System.out.println(new StringBuffer(t).reverse());
});
}
}

默认方法:default Consumer andThen(Consumer<? super T> after)

  • andThen方法谁在前面,谁先消费!
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConsumerDemo {
public static void method(String name, Consumer<String> consumer1, Consumer<String> consumer2) {
consumer1.andThen(consumer2).accept(name);
}

public static void main(String[] args) {
method("消费了这个字符串", (t)->{
System.out.println("第一次消费:" + new StringBuffer(t).reverse());
},(t)->{
System.out.println("第二次消费:" + new StringBuffer(t).reverse());
});
}
}

Predicate接口

  • Predicate接口的作用是对数据类型的数据进行判断,结果返回一个boolean值。

抽象方法:test(T t)

1
2
3
4
5
6
7
8
9
10
public class PredicateDemo {
public static boolean checkString(String str, Predicate<String> predicate) {
return predicate.test(str);
}

public static void main(String[] args) {
boolean b = checkString("abcdefg", (s)->s.length() > 5);
System.out.println(b);
}
}

默认方法:default Predicate and(Predicate<? super T> other)

  • 相当于逻辑表达式&&(与)
1
2
3
4
5
6
7
8
9
10
public class PredicateDemo {
public static boolean checkString(String str, Predicate<String> predicate1, Predicate<String> predicate2) {
return predicate1.and(predicate2).test(str);
}

public static void main(String[] args) {
boolean b = checkString("abcdefg", (s)->s.length() > 5,(s)->s.contains("a"));
System.out.println(b);
}
}

默认方法:default Predicate or(Predicate<? super T> other)

  • 相当于逻辑表达式||(或)
1
2
3
4
5
6
7
8
9
10
public class PredicateDemo {
public static boolean checkString(String str, Predicate<String> predicate1, Predicate<String> predicate2) {
return predicate1.or(predicate2).test(str);
}

public static void main(String[] args) {
boolean b = checkString("abcdefg", (s)->s.length() > 5,(s)->s.contains("h"));
System.out.println(b);
}
}

默认方法:default Predicate negate()

  • 相当于逻辑表达式!(非)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class PredicateDemo {
    public static boolean checkString(String str, Predicate<String> predicate) {
    return predicate.negate().test(str);
    }

    public static void main(String[] args) {
    boolean b = checkString("abcdefg", (s)->s.length() > 5);
    System.out.println(b);
    }
    }

Function接口

  • Function接口用来根据一个类型的数据得到另一个类型的数据。

抽象方法:R apply(T t)

1
2
3
4
5
6
7
8
9
10
public class FunctionDemo {
public static void change(String s, Function<String, Integer> fun) {
Integer i = fun.apply(s);
System.out.println(i);
}

public static void main(String[] args) {
change("56", (s)->Integer.parseInt(s));
}
}

默认方法:default Function<T, V> andThen(Function<? super R, ? extends V> after)

  • andThen方法谁在前面,谁先执行!
1
2
3
4
5
6
7
8
9
10
public class FunctionDemo {
public static void change(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
String str = fun1.andThen(fun2).apply(s);
System.out.println(str);
}

public static void main(String[] args) {
change("56", (s)->Integer.parseInt(s),(i)->i+"");
}
}
坚持原创技术分享,您的支持将鼓励我继续创作!
-------------这是我的底线^_^-------------