JDK8新特性(一)lambda表达式详解

lambda表达式是JAVA8中提供的一种新的特性,它支持JAVA也能进行简单的“函数式编程”。 
它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

那什么情况下需要使用该表达式呢,先看看我写的几个例子

第一种情况:比较器的使用

		Collections.sort(result,(s1,o)->{
			if(s1.getLessonDate().getTime()>o.getLessonDate().getTime()) {
				return 1;
			}else if(s1.getLessonDate().getTime()<o.getLessonDate().getTime()) {
				return -1;
			}else {
				int  hour = Integer.parseInt(s1.getTime().split(":")[0]);
				int oHour = Integer.parseInt(o.getTime().split(":")[0]);
				if(hour>oHour) {
					return 1;
				}else if(hour<oHour){
					return -1;
				}
			}
			return 0;
		});
Collections.sort(weeks,(w1,w2)->w1.getPeriod()>w2.getPeriod()?1:-1);

第二种情况:线程的使用

/**
 * 使用lambda表达式替换Runnable匿名内部类
 * @author MingChenchen
 *
 */
public class RunableTest {
    /**
     * 普通的Runnable
     */
    public static void runSomeThing(){

        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("I am running");
            }
        };
        new Thread(runnable).start();
    }

    /**
     * 使用lambda后的
     */
    public static void runSomeThingByLambda(){
        new Thread(() -> System.out.println("I am running")).start();
    }

    public static void main(String[] args) {
        runSomeThing();
//      runSomeThingByLambda();
    }
}

上述代码中:
() -> System.out.println("I am running")就是一个lambda表达式,
可以看出,它是替代了new Runnable(){}这个匿名内部类。

第三种情况:集合的使用

courseList.forEach(course->{
			JSONObject item = new JSONObject();
			String time = course.get("timeKey").toString().replace(",", " ");
			item.put("time", time);
			Integer weekItem = Integer.parseInt(course.get("weekItem").toString())+1;
			item.put("weekItem", weekItem);
			item.put("weekItemStr", WeekEnum.getName(weekItem));
			item.put("lessonDate", course.get("lessonDate"));
			item.put("week",DateUtil.getWeek((Date)course.get("lessonDate")));
			item.put("id", course.get("id"));
			result.add(item);
		});

什么时候可以使用它?

先说一个名词的概念:函数式接口:Functional Interface. 
定义的一个接口,接口里面必须 有且只有一个抽象方法 ,这样的接口就成为函数式接口。 
在可以使用lambda表达式的地方,方法声明时必须包含一个函数式的接口。 
(JAVA8的接口可以有多个default方法)

任何函数式接口都可以使用lambda表达式替换。 
例如:ActionListener、Comparator、Runnable

lambda表达式只能出现在目标类型为函数式接口的上下文中。

注意: 
此处是只能!!! 
意味着如果我们提供的这个接口包含一个以上的Abstract Method,那么使用lambda表达式则会报错。 
这点已经验证过了。

场景: 
这种场景其实很常见: 
你在某处就真的只需要一个能做一件事情的函数而已,连它叫什么名字都无关紧要。 
Lambda 表达式就可以用来做这件事。

几个特性

1. 类型推导 
编译器负责推导lambda表达式的类型。它利用lambda表达式所在上下文所期待的类型进行推导, 
这个被期待的类型被称为目标类型。就是说我们传入的参数可以无需写类型了!

2.变量捕获 
在Java SE 7中,编译器对内部类中引用的外部变量(即捕获的变量)要求非常严格: 
如果捕获的变量没有被声明为final就会产生一个编译错误。 
我们现在放宽了这个限制——对于lambda表达式和内部类, 
我们允许在其中捕获那些符合有效只读(Effectively final)的局部变量。

简单的说,如果一个局部变量在初始化后从未被修改过,那么它就符合有效只读的要求, 
换句话说,加上final后也不会导致编译错误的局部变量就是有效只读变量。

注意:此处和final关键字一样,指的是引用不可改!(感觉没多大意义,还不是用的final)

3.方法引用 
如果我们想要调用的方法拥有一个名字,我们就可以通过它的名字直接调用它。 
Comparator byName = Comparator.comparing(Person::getName); 
此处无需再传入参数,lambda会自动装配成Person类型进来然后执行getName()方法,而后返回getName()的String

方法引用有很多种,它们的语法如下:

静态方法引用:ClassName::methodName 
实例上的实例方法引用:instanceReference::methodName 
超类上的实例方法引用:super::methodName 
类型上的实例方法引用:ClassName::methodName 
构造方法引用:Class::new 
数组构造方法引用:TypeName[]::new

4.JAVA提供给我们的SAM接口 
Java SE 8中增加了一个新的包:java.util.function,它里面包含了常用的函数式接口,例如:

Predicate<T>——接收T对象并返回boolean
Consumer<T>——接收T对象,不返回值
Function<T, R>——接收T对象,返回R对象
Supplier<T>——提供T对象(例如工厂),不接收值
UnaryOperator<T>——接收T对象,返回T对象
BinaryOperator<T>——接收两个T对象,返回T对象

Predicate<T>——接收T对象并返回boolean
Consumer<T>——接收T对象,不返回值
Function<T, R>——接收T对象,返回R对象
Supplier<T>——提供T对象(例如工厂),不接收值
UnaryOperator<T>——接收T对象,返回T对象
BinaryOperator<T>——接收两个T对象,返回T对象


那么在参数为这些接口的地方,我们就可以直接使用lambda表达式了
 

猜你喜欢

转载自blog.csdn.net/zhuwei_clark/article/details/84572097