JDK8新特性之函数式接口

函数式接口

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

什么是函数式接口?

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

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

格式

public interface MyFunctionInterface {
	void method();
}

@FunctionalInterface注解

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

编译成功的情况:

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

编译失败的情况:

@FunctionalInterface		//编译失败
public interface MyFunctionInterface {
	void method();
	void method2();
}

Lambda表达式

Lambda表达式的重要特征:

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

Lambda表达式的使用

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

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表达式来重写这个接口中的方法,简化代码。

public static void show(MyFunctionInterface myInterface) {
	myInterface.method();
}

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

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

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表达式是延迟执行的,这正好可以作为解决方案,提升性能。

性能浪费的日志案例

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表达式的延迟执行优化这段代码

@FunctionalInterface
public interface MessageBuilder {
	String builderMessage();
}
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表达式来创建线程。

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

常用的函数式接口

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

Supplier接口

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

  • Supplier接口被称之为生产型接口,指定接口是什么泛型,就会生产什么类型的数据。
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)

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方法谁在前面,谁先消费!
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)

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)

  • 相当于逻辑表达式&&(与)
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)

  • 相当于逻辑表达式||(或)
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()

  • 相当于逻辑表达式!(非)
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)

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方法谁在前面,谁先执行!
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+"");
	}
}
发布了64 篇原创文章 · 获赞 67 · 访问量 6881

猜你喜欢

转载自blog.csdn.net/qq_45193304/article/details/104857829