Lambda表达式

Lambda表达式

1. Lambda表达式简介

Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

比如之前我们创建一个Runnable多线程程序需要这样写:

//使用Runnable接口创建线程
Thread th1 = new Thread( new Runnable(){
    public void run(){
        System.out.println("aaa");
    }
});
//启动线程
th1.start();	

那么使用Lambda表达式后代码会变成这样:

//lambda表达式
Thread th2 = new Thread( () -> System.out.println("bbb") );
//启动线程
th2.start();

当然还可以更简单

//更简单的写法
new Thread( () -> System.out.println("ccc") ).start();

其中 ( ) 用来描述参数列表,{ } 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。从这个例子中我们可以看出Lambda表达式的一些特点:

  • JDK 提供了大量的内置函数式接口供我们使用,使得Lambda表达式的运用更加方便、高效。
  • Lambda表达式,也可以称为闭包,它是推动Java8发布的最重要新特性。
  • Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
  • 使用Lambda表达式可以使代码变的更加简洁紧凑。

2. Lambda表达式语法

Lambda表达式的语法为:

( 参数列表 ) -> {方法体}

( ) : 用于描述参数列表;

{ } : 用于描述方法体;

-> : Lambda运算符,可以叫做箭头符号,或者 goes to

  • 对接口的要求

    虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法

    不过jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用

  • @FunctionalInterface

    修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。比如我们上个例子用到的Runnable接口源码中就有这个注解。

    @FunctionalInterface
    public interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
        public abstract void run();
    }
    

这里我们根据接口方法参数,有无返回值,罗列6种只包含单个方法的接口,分别使用Lambda表达式来实现。

  1. 无参数,无返回值
interface Source01{
    //无参数  无返回值
    void test();
}

public static void main(String[] args) {
    Source01 s01 = ( )->{ 
        System.out.println("无参数  无返回值");
    };
    s01.test();
}//main()
  1. 有参数,无返回值
interface Source02{
    //有参数  无返回值
    void test(int a);
}

public static void main(String[] args) {
    Source02 s02 = (int a)->{ 
        System.out.println("有参数 "+a+"  无返回值");
    };
    s02.test(10);
}//main()
  1. 有多个参数,无返回值
interface Source03{
    //有多个参数  无返回值
    void test(int a,String b);
}

public static void main(String[] args) {
    Source03 s03 = (int a,String b)->{ 
        System.out.println("有多个参数 "+a+","+b+"  无返回值");
    };
    s03.test(10,"张三");
}//main()
  1. 无参数,有返回值
interface Source04{
    //无参数  有返回值
    int test( );
}

public static void main(String[] args) {
    Source04 s04 = ( )->{ 
        System.out.println("无参数 有返回值");
        return a;
    };
    int ret = s04.test();
    System.out.println( ret );
}//main()
  1. 有参数,有返回值
interface Source05{
    //有参数  有返回值
    int test( int a );
}

public static void main(String[] args) {
    Source05 s05 = (int a)->{ 
        System.out.println("有参数 "+a+"  有返回值");
        return a*100 ;
    };
    int ret=s05.test(88);
    System.out.println( ret );
}//main()
  1. 有多个参数,有返回值
interface Source06{
    //有多个参数  有返回值
    int test( int a , int b );
}

public static void main(String[] args) {
    Source06 s06 = (int a,int b)->{ 
        System.out.println("有多个参数 "+a+","+b+"   有返回值");
        return a*b ;
    };
    int ret=s06.test(8,8);
    System.out.println( ret );
}//main()

3. Lambda表达式精简语法

Lambda表达式可以将代码进一步简化,写出更加优雅的代码,规则如下:

  • 参数类型可以省略
  • 只有一个参数时,( ) 可以省略
  • 只有一条语句时,{ } 可以省略
  • 只有一条return语句时,{ } 和 retrun 可以省略

这样我们将上一节6个接口的实现重新编写为:

//只有一条语句
Source01 s01 = ( ) -> System.out.println("无参数  无返回值");
s01.test();

//省略参数类型	+	只有一个参数	+	只有一条语句 
Source02 s02 = a -> System.out.println("有参数 "+a+"  无返回值");
s02.test(10);

//省略参数类型	+	只有一条语句
Source03 s03 = ( a,b ) -> System.out.println("有参数 "+a+","+b+"  无返回值");
s03.test(10,"李四");

//只有一条return语句
Source04 s04 = ( )-> 88;
int ret04 = s04.test();
System.out.println( ret04 );

//省略参数类型	+	只有一个参数	+	只有一条return语句 
Source05 s05 =  a -> a *100;
int ret05 = s05.test(88);
System.out.println( ret05 );

//省略参数类型	+	只有一条return语句 
Source06 s06 = (a, b)-> a*b ;
int ret06 = s06.test(8,8);
System.out.println( ret06 );

4. Lambda表达式常用引用

4.1 方法引用

我们在编写代码时,有时会出现多个Lambda表达式实现函数式接口代码一致的情况,为了便于维护,我们可以封装成通用方法,这时就可以用方法引用实现,语法为:

对象 : : 方法

如果是static方法,语法为:

类名 : : 方法

比如我们在开发中有这样的场景:

interface Source07{
    int test( int a );
}

public static void main(String[] args) {
	
    Source07 s07 = a -> a + 10;
    System.out.println( s07.test(10) );

    //...若干行其他代码后
    //又出现对Source07的实现且业务功能一致
    Source07 s08 = a -> a + 10;
    System.out.println( s07.test(20) );

}//main()

此时就可以将实现封装起来,如果封装到类中我们可以编写如下方法:

public int sourceTest(int a){
    return a + 10;
}

然后再次使用相同实现时,可以这样使用:

Source07 s09 = lmd03::sourceTest;
System.out.println( s07.test(30) );

完整代码如下:

class Lambda03 {
		
	interface Source07{
		int test( int a );
	}

	public static void main(String[] args) {
		
		Source07 s07 = a -> a + 10;
		System.out.println( s07.test(10) );

		//...若干行其他代码后
		//又出现对Source07的实现且业务功能一致
		Source07 s08 = a -> a + 10;
		System.out.println( s07.test(20) );

		//使用方法引用的代码
		Lambda03 lmd03 = new Lambda03();

		Source07 s09 = lmd03::sourceTest;
		System.out.println( s07.test(30) );

		Source07 s10 = lmd03::sourceTest;
		System.out.println( s07.test(40) );

	}//main()
	
    //封装通用接口实现
	public int sourceTest(int a){
		return a + 10;
	}

}

当然我们也可以将通用方法封装成 static,使用时用类名 : : 方法的方式:

class Lambda03 {
		
	interface Source07{
		int test( int a );
	}

	public static void main(String[] args) {
        
		Source07 s07 = a -> a + 10;
		System.out.println( s07.test(10) );

		//...若干行其他代码后
		//又出现对Source07的实现且业务功能一致
		Source07 s08 = a -> a + 10;
		System.out.println( s07.test(20) );

		//使用静态方法引用的代码
		Source07 s09 = Lambda03::sourceTest;
		System.out.println( s07.test(30) );

		Source07 s10 = Lambda03::sourceTest;
		System.out.println( s07.test(40) );

	}//main()
	
    //封装static通用接口实现
	public static int sourceTest(int a){
		return a + 10;
	}
}

运行结果:

20
30
40
50

4.2 构造方法引用

这种方式我们需要声明接口,该接口作为对象的生成器,通过 对象名::new 的方式来实例化对象,然后调用方法返回对象,语法为:

对象类名 : : new

比如先定义一个员工 Employee 类,实现两个构造方法:

class Employee {
	private String empName;
	private int empLevel;
	
	public Employee() {
		super();
		System.out.println("创建 无参 Employee");
	}
	public Employee(String empName, int empLevel) {
		super();
		this.empName = empName;
		this.empLevel = empLevel;
        System.out.println("创建 有参数的 Employee");
	}

	public String getEmpName() {
		return empName;
	}
	public void setEmpName(String empName) {
		this.empName = empName;
	}
	public int getEmpLevel() {
		return empLevel;
	}
	public void setEmpLevel(int empLevel) {
		this.empLevel = empLevel;
	}
	
	@Override
	public String toString(){
		return this.empName +" " + this.empLevel;
	}
}

然后我们创建两个对象生成器接口:

//无参构造器引用接口
interface EmployeeCrt{
	Employee getEmployee();
}
//有参构造器引用接口
interface EmployeeCrtMult{
	Employee getEmployee(String name, int level);
}

使用时,我们分别用传统的Lambda表达式和构造器引用来创建3个对象:

//之前的方式
EmployeeCrt crt1 = ()-> new Employee();
System.out.println( crt1.getEmployee() );

//使用封装好的构造器引用(无参的)
EmployeeCrt crt2 = Employee::new;			//对象类名::new
System.out.println( crt2.getEmployee() );	//调用生成器方法返回对象

//多个参数的构造器引用
EmployeeCrtMult crt3 = Employee::new;		//对象类名::new
System.out.println( crt3.getEmployee("刘经理",5) );//调用生成器方法返回对象

运行结果:

创建 无参 Employee
null 0
创建 无参 Employee
null 0
创建 有参数的 Employee
刘经理 5

5. FunctionalInterface注解

函数式接口注解,表示这是一个只有一个抽象方法的接口。也叫做SAM接口,即 Single Abstract Method interfaces,特点有:

  • 接口有且仅有一个抽象方法
  • 允许定义静态方法
  • 允许定义默认方法
  • 允许java.lang.Object中的public方法
  • 对于符合函数式接口定义的接口,不加该注解不影响使用,加上能够更好地让编译器进行检查;如果不符合函数式接口定义的接口,加上@FunctionalInterface会报错。

这里我们给出一个例子:

@FunctionalInterface
interface fctItfcTest{

	//一个抽象方法
	int method1();

	//java.lang.Object中的public方法
	public boolean equals(Object obj);

	//默认方法
	public default void defMethod(){
	
	}

	//静态方法
	public static void sticMethod(){
	
	}

}

下面这个例子加上@FunctionalInterface是错误的:

//去掉注解 是正确的接口但不是 函数式接口
//加上注解 会出现编译错误
interface fctItOtherTest{
    
    void add( );
    
    void delete( );
    
}

6. 系统内置的函数式接口

Java8的推出,是以Lambda这个重要特性一起推出的,因此在java.util.function包下系统也内置了一系列的函数式接口,大家可以自行查看。

7. Lambda表达式应用举例

对象排序:

ArrayList<Employee> empList = new ArrayList<Employee>();
empList.add( new Employee("w经理",6) );
empList.add( new Employee("x经理",5) );
empList.add( new Employee("l经理",1) );
empList.add( new Employee("z经理",4) );
empList.add( new Employee("c经理",2) );

/*	传统匿名内部类写法 	ArrayList.sort(Comparator<? super E> c)
	empList.sort( new Comparator<Employee>(){
		public int compare(Employee e1,Employee e2){
			return e1.getEmpLevel() - e2.getEmpLevel();
		}
	});
*/
//Lambda写法 			ArrayList.sort(Comparator<? super E> c)
empList.sort( (e1,e2)-> e1.getEmpLevel() - e2.getEmpLevel() );

/*	传统的forEach写法
	for(Employee e : empList){
    	System.out.println( e );
	}
*/
//lambda			ArrayList.forEach(Consumer<? super E> action)
empList.forEach( e -> System.out.println( "lambda foreach " + e ));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值