JDK1.8新特性(1)Lambda 表达式与函数式接口

简介

Lambda 表达式是一种匿名函数,简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。

你可以将其想做一种速记,在你需要使用某个方法的地方写上它。当某个方法只使用一次,而且定义很简短,使用这种速记替代之尤其有效,这样,你就不必在类中费力写声明与方法了。

一、Lambda 表达式的结构

  1. 一个 Lambda 表达式可以有零个或多个参数
  2. 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
  3. 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
  4. 空圆括号代表参数集为空。例如:() -> 42
  5. 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
  6. Lambda 表达式的主体可包含零条或多条语句
  7. 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
  8. 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
前置语法
无参数无返回值() -> System.out.println(“Hello WOrld”)
有一个参数无返回值(x) -> System.out.println(x)
有且只有一个参数无返回值x -> System.out.println(x)
有多个参数,有返回值,有多条lambda体语句(x,y) -> {System.out.println(“xxx”);return xxxx;};
有多个参数,有返回值,只有一条lambda体语句(x,y) -> xxxx

口诀:左右遇一省括号,左侧推断类型省
注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念

二、什么是函数式接口

Java 中,Marker(标记)类型的接口是一种没有方法或属性声明的接口,简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口。
java.lang.Runnable 就是一种函数式接口,在 Runnable 接口中只声明了一个方法 void run()我们使用匿名内部类来实例化函数式接口的对象,有了 Lambda 表达式,这一方式可以得到简化.
例如,我们可以通过 Lambda 表达式创建 Runnable 接口的引用:

Runnable r = () -> System.out.println("hello world");

当不指明函数式接口时,编译器会自动解释这种转化:

new Thread(
  	 () -> System.out.println("hello world")
).start();

因此,在上面的代码中,编译器会自动推断:根据线程类的构造函数签名 public Thread(Runnable r) { },将该 Lambda 表达式赋给 Runnable 接口。

并且还提供了注解:@FunctionalInterface

常见的四大函数式接口:

  • Consumer 《T》:消费型接口,有参无返回值
    @Test
    public void test(){
        changeStr("hello",(str) -> System.out.println(str));
    }
    
    /**
     *  Consumer<T> 消费型接口
     * @param str
     * @param con
     */
    public void changeStr(String str, Consumer<String> con){
        con.accept(str);
    }
  • Supplier 《T》:供给型接口,无参有返回值
    @Test
    public void test2(){
        String value = getValue(() -> "hello");
        System.out.println(value);
    }

    /**
     *  Supplier<T> 供给型接口
     * @param sup
     * @return
     */
    public String getValue(Supplier<String> sup){
        return sup.get();
    }
  • Function 《T,R》::函数式接口,有参有返回值
    @Test
    public void test3(){
        Long result = changeNum(100L, (x) -> x + 200L);
        System.out.println(result);
    }

    /**
     *  Function<T,R> 函数式接口
     * @param num
     * @param fun
     * @return
     */
    public Long changeNum(Long num, Function<Long, Long> fun){
        return fun.apply(num);
    }
  • Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型
public void test4(){
        boolean result = changeBoolean("hello", (str) -> str.length() > 5);
        System.out.println(result);
    }

    /**
     *  Predicate<T> 断言型接口
     * @param str
     * @param pre
     * @return
     */
    public boolean changeBoolean(String str, Predicate<String> pre){
        return pre.test(str);
    }

在四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。

总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了

三、Lambda 表达式举例

学习 Lambda 表达式的最好方式是学习例子。

  • 线程可以通过以下方法初始化:
//旧方法:
new Thread(new Runnable() {
@Override
public void run() {
    System.out.println("Hello from thread");
}
}).start();
 
//新方法:
new Thread(
() -> System.out.println("Hello from thread")
).start();
  • set集合排序:
 //旧方法
 Comparator<Integer> cpt = new Comparator<Integer>() {
       @Override
       public int compare(Integer o1, Integer o2) {
           return Integer.compare(o1,o2);
       }
   };
   TreeSet<Integer> set = new TreeSet<>(cpt);

	
   //新方法
   Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y);
   TreeSet<Integer> set2 = new TreeSet<>(cpt2);
  • 以下代码的作用是打印出给定数组中的所有元素。
  • 使用 Lambda 表达式的方法不止一种,在下面的例子中,我们先是用常用的箭头语法创建 Lambda 表达式,之后,使用 Java 8 全新的双冒号(::)操作符将一个常规方法转化为 Lambda 表达式:
	//Old way:
	List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
		for(Integer n: list) {
			System.out.println(n);
		}
	//New way:
	List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
	list1.forEach(n -> System.out.println(n));
	
	
	//or we can use :: double colon operator in Java 8
	list.forEach(System.out::println);

四、Lambda 表达式与匿名类的区别

使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类。

Lambda 表达式与匿名类的另一不同在于两者的编译方法。Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,它使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法,关于 Java 如何将 Lambda 表达式编译为字节码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值