在劫

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Java Lambda表达式

概念

函数式接口是指:有且仅有一个抽象方法的接口。

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

格式

只要确保接口的有且仅有一个抽象方法即可:

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

接口中的public abstract可以省略,所以格式可以简化:

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

函数式接口可以作为方法的参数和返回值类型

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

检测该函数是否是函数式接口,是否有且仅有一个抽象方法。

自定义函数式接口
public class LambdaTest{
	public static void useLambdaInterfaceMethod(LambdaInterface lifm){
		lifm.LambdaInterfaceMethod();
	}
	public static void main(String[] args){
		//传入接口的实现类:
		//useLambdaInterfaceMethod(new 接口的实现类);
		
		//传入接口的匿名内部类
		/*
		useLambdaInterfaceMethod(new LambdaInterface(){
			public void LambdaInterfaceMethod(){
				System.out.println("这里传入的是接口的实现匿名内部类");
			}
		});
		*/
		//抽象方法有且仅有一行语句,可以省略大括号
		//ustLambdaInterfaceMethod(() -> System.out.println("..."));
		useLambdaInterfaceMethod(() -> {
			System.out.println("使用Lambda表达式,重写接口中的抽象方法。")
				}
			);
	}
}
//注解FunctionanInterface检测是否为函数式接口
@FunctionalInterface
interface LambdaInterface{
	void LambdaInterfaceMethod();
}

函数式编程

Lambda的延迟执行

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

性能浪费的日志案例

日志可以快速定位问题,记录程序运行中的情况,以便项目的监控和优化。

一种场景是对参数进行有条件的使用,例如在日子消息拼接以后,在满足条件时打印输出。

/*
	下面存在性能浪费的问题
	调用log方法的时候,传入第二个参数是一个拼接后的字符串
	是先把字符串拼接好,然后再调用log方法
	log方法中的日志等级不是1级,那么就不会是如此拼接后的字符串
	这里的字符串就白拼接了,造成浪费
*/
public class LoggerTest{
	public static void log(int level, String msg){
		if(level == 1){
			System.out.println(msg);
		}
	}
	public static void main(String[] args){
		String str1 = "hello";
		String str2 = "java";
		log(1, str1 + str2);
	}
}
体验Lambda的更优写法
/*
	使用Lambda优化日志:
		延迟加载
	使用前提:必须存在函数式接口
	使用Lambda表达式作为参数传递,仅仅是把参数传递到log方法中,
	只有满足条件,日志的等级是1级
		才会调用接口LambdaInterface方法中的msgAdd()方法
		然后进行方法的拼接
	如果条件不满足,那么接口LambdaInterface方法中的msgAdd()方法不会执行
		拼接字符串的代码也不会执行,不会存在性能的浪费
*/
public class LoggerTest{
	public static void log(int level, LambdaInterface li){
		if(level == 1){
			String str = li.msgAdd();
			System.out.println(str);
		}
	}
	public static void main(String[] args){
		String str1 = "hello";
		String str2 = "java";
		log(1, () -> {
			//返回一个拼接好的字符串
			return str1 + str2;
		});
	}
}
@FunctionalInterface
interface LambdaInterface{
	//定义一个拼接消息的方法,返回拼接的消息
	String msgAdd();
}
证明Lambda的延迟
	public static void main(String[] args){
		String str1 = "hello";
		String str2 = "java";
		log(1, () -> {
			//没有满足条件这里不会执行
			System.out.println("不满足条件");
			//返回一个拼接好的字符串
			return str1 + str2;
		});
	}
使用Lambda作为参数和返回值

Java的Lambda表达式可以被当做匿名内部类的替代品,如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。
例如:Runnable接口就是一个函数式接口

public class Demo{
	private static void startThread(Runnable runab){
		new Thread(runab).start();
	}
	public static void main(String[] args){
		startThread(() -> System.out.println(...));
	}
}
public  class DemoComparator{
	//返回值类型是一个函数式接口
	//Comparator只有一个抽象方法,所以是函数式接口
	private static Comparator<String> newComparator(){
		return (a, b) -> b.length() - a.length();
	}
	public static void main(String[] args){
		String[] arr = {"a", "abs", "dfae"};
		//排序
		Arrays.sort(arr, newComparator());
	}
}

如果返回值类型是一个函数式接口,就可以直接返回一个Lambda表达式,如上程序。

注:Comparator有两个抽象方法,int cimpare(T o1, T o2)和boolean equals(Object obj),如果接口声明了一个覆盖java.lang.Object的全局方法之一的抽象方法,那么它不会计入接口的抽象方法数量中,因为接口的任何实现都将具有java.lang.Object或其他地方的实现。

常用的函数式接口

Supplier接口

java.util.function.Supplier接口包含一个无参的方法:T get();用来获取一个泛型参数指定类型的对象。

import java.util.function.Supplier;
public class SupplierClass{
	public static String getString(Supplier<String> sup){
		return sup.get();
	}
	public static void main(String[] args){
		String str1 = "hello";
		String str2 = "java";
		String getStringMethod = getString(() -> str1 + str2);
		System.out.println(getStringMethod);
	}
}

Supplier被称为生型接口,指定接口泛型是什么类型,接口中的get方法就返回什么类型。

求数组最大值练习
import java.util.function.Supplier;
public class SupplierClass{
	//返回的是Integer类型
	public static Integer getMax(Supplier<Integer> sup){
		return sup.get();
	}
	public static void main(String[] args){
		int[] iArr = {12, 252, -12, 435};
		int getMaxInteger = getMax(() -> {
			int max = iArr[0];
			//遍历数组
			for(int i : iArr){
				//如果数组有一个值大于max,则把这个值赋值给max
				if(i > max){
					max = i;
				}
			}
			//返回数组中的最大值
			return max;
		});
		System.out.println(getMaxInteger);
	}
}
Consumer接口

java.util.function.Consumer接口消费一个数据,而不是生产一个数据,其数据类型由泛型指定。包含抽象方法accept(T t)

import java.util.function.Consumer;
public class ConsumerClass{
	public static void consumerUseMethod(Consumer<String> com){
		con.accept("被使用的字符串");
	}
	public static void main(String[] args){
		consumerUseMethod((x) -> System.out.println(x));
	}
}
抽象方法:Accept
import java.util.function.Consumer;
public class ConsumerClass{
	public static void consumerUseMethod(String name,Consumer<String> con){
		con.accept(name);
	}
	public static void main(String[] args){
		consumerUseMethod("lilei", (x) -> System.out.println(x));
	}
}
抽象默认方法:andThen

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

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

java.util.Objects和requireNonNull静态方法将会在参数为null时主动抛出异常。省去重写if语句和抛出空指针异常的麻烦。

import java.util.function.Consumer;
/*
	Consumer接口默认方法andThen
	作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,对数据进行消费
	
	例如:
	Consumer<String> con1
	Consumer<String> con2
	String s = “hello”;
	con1.accept(s);
	con2.accept(s);
	连接两个Consumer接口,再进行消费
	con1.andThen(con2).accept(s);
*/
public class ConsumerClass{
	public static void consumerUseMethod(String name,
					Consumer<String> con, Consumer<String> con2){
		//con.accept(name);
		//con2.accept(name);
		//使用andThen方法替代上面代码:
		con.andThen(con2).accept(name);
		//con链接的con2,所以先执行con,再执行con2
	}
	public static void main(String[] args){
		consumerUseMethod(
			"lilei", 
			//第一个Lambda表达式
			(x) -> System.out.println(x),
			//第二个Lambda表达式
			(x) -> System.out.println("第二个Lambda表达式")
			);
	}
} 

如果需要多个Consumer接口,使用andThen方法,就是con1.andThen(con2).andThen(con3)...andThen(conN).accept(t);

Predicate接口

包含一个抽象方法:boolean test(T t),用于条件判断场景
符合条件:返回true
不符合:返回false

import java.util.function.Predicate;

public class PredicateClass{
	public static boolean checkString(String s, Predicate<String> pre){
		//传入要判断的文本
		return pre.test(s);
	}
	public static void main(String[] args){
		String str = "guess how long";
		//判断字符串长度是否大于5,并返回结果
		boolean b = checkString(str, (String s) -> {return str.length() > 5;});
		System.out.println(b);
	}
}

省略一些代码

		//省略String s 中类型,可以直接推导
		//只有一行语句,省略大括号和return关键字
		boolean b = checkString(str, s-> str.length() > 5);
默认方法and(与)

将两个Predicate条件使用与逻辑连接起来实现并且的效果,使用默认方法and

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

实现:

import java.util.function.Predicate;

public class PredicateClass{
	public static void checkString(String str, Predicate<String> preOne, Predicate<String> preTwo){
		//preOne.test("str")&&preTwo.test("str");
		boolean b = preOne.and(preTwo).test(str);
		System.out.println(b);
	}
	public static void main(String[] args){
		String strName = "helloworld";
		checkString(strName, s-> s.contains("H"),s-> s.contains("W"));
	}
}
默认方法or(或)
default Predicate<T> or(Predicate<? super T> other){
	Objects.requireNonNull(Other);
	return (t) -> test(t) || other.test(t);
}

实现:

import java.util.function.Predicate;

public class PredicateClass{
	public static boolean checkString(String str, Predicate<String> preOne, Predicate<String> preTwo){
		//等价于preOne.test(t) || preTwo.test(t);
		return preOne.or(preTwo).test(str);
	}
	public static void main(String[] args){
		String strName = "helloworld";
		boolean b = checkString(strName, s-> s.contains("h"),s-> s.contains("W"));
		System.out.println(b);
	}
}
默认方法negate(非)
default Predicate<T> negate(){
	retuan (t) -> !test(t);
}
		//取反
		return preOne.negate(preTwo).test(str);
Function接口

用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

抽象方法apply

Function最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。

import java.util.function.Function;
public class FunctionClass{
	//Function<原始类型, 要转换的类型>
	public static void applyMethod(String str, Function<String, Integer> fun){
		//字符串类型转换成int类型
		int num = fun.apply(str);
		System.out.println(num + 20);
	}
	public static void main(String[] args){
		String str = "100";
		applyMethod(str, s -> Integer.parseInt(s));
	}
}
默认方法andThen
//用来组合操作
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after){
	Objects.requireNonNull(after);
	return (T t) -> after.apply(apply(t));
}

实现代码:

import java.util.function.Function;
public class FunctionClass{
	//andThen组合操作
	public static void applyMethod(String str, Function<String, Integer> funOne, Function<Integer, String> funTwo){
		//字符串类型转换成int类型,加10
		//int num = fun.apply(str) + 10;
		//然后转换成String类型
		//String str = funTwo.apply(num);
		
		//组合,先实现funOne,再实现funTwo
		String strr = funOne.andThen(funTwo).apply(str);
		System.out.println(strr);
	}
	public static void main(String[] args){
		String str = "100";
		//先把字符串转换为integer,结果加10
		//把前面funOne的结果转换成String类型
		applyMethod(str, (String s) -> Integer.parseInt(s) + 10, (Integer in) -> String.valueOf(in));
	}
}

Lambda方法引用

方法引用符号

双冒号::是为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达是函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。

语义分析

例如:System.out对象中有一个重载的println(String)方法恰好就是我们所需要的,那么对于该方法的函数式接口参数,以下两种方式等价:

Lambda表达式: s -> System.out.println(s);
方法引用写法: System.out::println

第一种语义是:拿到参数之后经Lambda手,继而传递给System.out.println方法去处理;
第二种是:直接让System.out中的println方法来取代Lambda。

两种写法的执行效果完全一样。而第二种方法引用复用了已有方案,更加简洁。

Lambda中传递的参数一定是方法引用中那个方法可以接收的类型,否则抛出异常。

推导与省略

如果使用Lambda,那么根据可推导就是可省略的原则,无需指定参数类型,也无需指定重载形式--它们都是自动推导,而如果使用方法引用,也是同样可以根据上下文进行推导。
函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟。

通过对象名引用成员方法
/*
	通过对象名引用成员方法
	使用前提:对象名已经存在,成员方法已经存在
*/

public class LambdaObjectUseMethod{
	//定义一个方法
	public static void printString(Printable p){
		p.print("hello");
	}
	public static void main(String[] args){
		//Lambda表达式
		printString(x -> {
			MethodUpper mu = new MethodUpper();
			mu.printUpperCaseString(x);
		});
		
		//方法引用
		//对象必须已经存在
		MethodUpper mu2 = new MethodUpper();
		printString(mu2::printUpperCaseString);
	}
}

//函数式接口
@FunctionalInterface
interface Printable{
	void print(String s);
}



//将字符串全改为大写并输出
class MethodUpper{
	public void printUpperCaseString(String str){
		System.out.println(str.toUpperCase());
	}
}
通过类名称引用静态方法
/*
	通过对象名引用成员方法
	使用前提:类已经存在,静态方法已经存在
*/

public class LambdaObjectUseMethod{
	//定义一个方法
	public static void printString(Printable p){
		p.print("hello");
	}
	public static void main(String[] args){
		//通过类名引用静态方法
		printString(MethodUpper::printUpperCaseString);
	}
}

//函数式接口
@FunctionalInterface
interface Printable{
	void print(String s);
}



//将字符串全改为大写并输出
class MethodUpper{
	//静态方法
	public static void printUpperCaseString(String str){
		System.out.println(str.toUpperCase());
	}
}
通过super引用成员方法
//函数式接口
@FunctionalInterface
interface Greetable{
	void greet();
}

//父类
class FatherClass{
	public void showMethod(){
		System.out.println("我是你爸爸");
	}
}

public class SonClass extends FatherClass{
	public void showMethod(){
		System.out.println("不符solo!!!");
	}
	
	public void useGreetable(Greetable gt){
		gt.greet();
	}
	public void show(){
		/*
		useGreetable(() -> {
			FatherClass fc = new SonClass();
			super.showMethod();
		});
		*/
		//super引用成员方法
		useGreetable(super::showMethod);
	}
	
	public static void main(String[] args){
		new SonClass().show();
	}
}
通过this引用成员方法
//函数式接口
@FunctionalInterface
interface Greetable{
	void greet();
}

//父类
class FatherClass{
	public void showMethod(){
		System.out.println("我是你爸爸");
	}
}

public class SonClass extends FatherClass{
	public void showMethod(){
		System.out.println("不符solo!!!");
	}
	
	public void useGreetable(Greetable gt){
		gt.greet();
	}
	public void show(){
		//this引用本类成员方法
		useGreetable(this::showMethod);
	}
	
	public static void main(String[] args){
		new SonClass().show();
	}
}
类构造器的引用

构造器引用使用 类名称::new 的格式

//类构造器引用
class Person{
	String name;
	public Person(String name){
		this.name = name;
	}
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
}

@FunctionalInterface
interface CreatePerson{
	Person returnPerson(String name);
}

public class PersonFunctionClass{
	public static void method(String name, CreatePerson cp){
		Person p = cp.returnPerson(name);
		//Person p = 
		System.out.println(p.getName());
	}
	public static void main(String[] args){
		/*Lambda表达式
		method("lilei",(String name) -> {
			return new Person(name);
		});
		*/
		
		/*简化版的Lambda表达式
		method("lilei", name -> new Person(name));
		*/
		
		//构造器引用
		method("lilei", Person::new);
	}
}
数组构造器的引用
//数组的构造器引用
@FunctionalInterface
interface ArrayBuiler{
	//创建int数组类型的方法
	int[] createIntArray(int length);
}

//数组的构造器引用
public class ArrayTestClass{
	//定义一个方法,方法参数传递创建数组的长度和ArrayBuiler接口
	//方法内部根据传递的长度创建数组
	public static int[] createArray(int length, ArrayBuiler ab){
		return ab.createIntArray(length);
	}
	
	public static void main(String[] args){
		int[] arr1 = createArray(10, (int i) -> {
			return new int[i];
		});
		
		int[] arr2 = createArray(12, len -> new int[len]);
		
		//引用数组的构造器
		int[] arr3 = createArray(13, int[]::new);
	}
}
posted on 2019-07-29 16:40  在劫  阅读(166)  评论(0编辑  收藏  举报