JAVA核心--Lambda表达式

        Lambda表达式的使用可以很大程度上减少代码的数量,但是阅读起来并不是怎么方便,如果是长期查看Lambda代码可能还好一点!!!使用Lambda表达式减少的代码主要是简化匿名内部类使用的代码的数量,Lambda表达式的书写方式: 
  • (参数) -> 表达式 
  • (参数) -> 语句 
  • (参数) -> { 语句 }

1.使用lambda表达式替代匿名内部类

        毫无疑问,Lambda表达式用得最多的场合就是替代匿名内部类,而实现Runnable接口是匿名内部类的经典例子。Lambda表达式的功能相当强大,用()->就可以代替整个匿名内部类!

    以下代码将让你感受到Lambda表达式的方便

public class LambdaDemo {
	//使用匿名内部类
    public static void oldRunable() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("The old runable now is using!");
            }
        }).start();
    }
    //使用lambda表达式
    public static void newRrunable() {
        new Thread(() -> System.out.println("It's a lambda function!")).start();
    }
    
    public static void main(String[] args) {
    	//调用使用匿名内部类的函数
		LambdaDemo.oldRunable();
		
		//调用使用lambda表达式的函数
		LambdaDemo.newRrunable();
	}
}

运行结果:


是不是强大到可怕?是不是简单到可怕?是不是清晰明了重点突出到可怕?这就是Lambda表达式的可怕之处,用极少的代码完成了之前一个类做的事情!

2.使用lambda表达式对集合进行迭代

        Java的集合类是日常开发中经常用到的,常见的操作就是进行迭代,并将业务逻辑应用于各个元素,例如处理订单、交易和事件的列表。由于Java是命令式语言,Java 8之前的所有循环代码都是顺序的,即可以对其元素进行并行化处理。如果你想做并行过滤,就需要自己写代码,这并不是那么容易。通过引入Lambda表达式和默认方法,将做什么和怎么做的问题分开了,这意味着Java集合现在知道怎样做迭代,并可以在API层面对集合元素进行并行处理。下面的例子里,我将介绍如何在使用Lambda或不使用Lambda表达式的情况下迭代列表。你可以看到列表现在有了一个 forEach()  方法,它可以迭代所有对象,并将你的lambda代码应用在其中。请看对比:

import java.util.Arrays;
import java.util.List;

public class IterDemo {
	public static void main(String[] args) {
        List<String> languages = Arrays.asList("java","scala","python");
        //before java8
        for(String each:languages) {
            System.out.println(each);
        }
        //after java8
        System.out.println("--------");
        languages.forEach(x -> System.out.println(x));
        languages.forEach(System.out::println);
    }
}

运行结果:


列表循环的最后一个例子展示了如何在Java 8中使用方法引用(method reference)。你可以看到C++里面的双冒号、范围解析操作符现在在Java 8中用来表示方法引用。

3.使用lambda表达式和函数式接口Map与Reduce

    中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象。

import java.util.Arrays;
import java.util.List;

public class MapDemo {
	public static void main(String[] args) {
		//不使用lambda表达式为每个数增加15%
		List<Integer> numbers = Arrays.asList(100, 200, 300, 400, 500);
		for (Integer num : numbers) {
		    double answer = num + .15 * num;
		    System.out.println(answer);
		}
		System.out.println("-------"); 
		//使用lambda表达式
		numbers.stream().map((num) -> num + .15 * num).forEach(System.out::println);
	}	
}

运行结果:


在上面代码中,可以看到map将集合类(例如列表)元素进行转换,通过map方法将number中的每个数增加了0.15倍的大小然后依次输出。还有一个 reduce() 函数可以将所有值合并成一个。Map和Reduce操作是函数式编程的核心操作,因为其功能,reduce 又被称为折叠操作。

import java.util.Arrays;
import java.util.List;

public class ReduceDemo {
	public static void main(String[] args) {
		//不使用lambda表达式为每个数增加15%
		List<Integer> numbers = Arrays.asList(100, 200, 300, 400, 500);
		double total = 0;
		for (Integer num : numbers) {
		    total += num + .15 * num;
		}
		System.out.println("Total : " + total);
		 
		//使用lambda表达式
		double bill = numbers.stream().map((num) -> num + .15 * num).reduce((sum, add) -> sum + add).get();
		System.out.println("Total : " + bill);
	}
}

运行结果:


在上面代码中,可以看到通过reduce方法将number中的每个数增加了0.15倍的大小然后计算总和输出相信大家课可以看出,用map+reduce+lambda表达式的写法高出不止一个level。

4.使用lambda表达式和函数式接口Predicate与Filter

    Predicate函数式接口以及Lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。下面是有关Predicate和Filter的例子,展示了过滤集合数据的多种常用方法。Predicate接口非常适用于做过滤。

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class PredicateDemo {
	public static void main(String args[]){
	    List<String> names = Arrays.asList("Chenjie", "Zhanghe", "Ajia", "Runtu", "Lisi");
	 
	    System.out.println("names which starts with C :");
	    filter(names, (str) -> str.startsWith("C"));
	 
	    System.out.println("names which ends with e ");
	    filter(names, (str) -> str.endsWith("e"));
	 
	    System.out.println("Print all names :");
	    filter(names, (str) -> true);
	 
	    System.out.println("Print no names : ");
	    filter(names, (str) -> false);
	 
	    System.out.println("Print names whose length greater than 4:");
	    filter(names, (str) -> str.length() > 4);
	}
	 
	public static void filter(List<String> names, Predicate<String> condition) {
	    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
	        System.out.println(name + " ");
	    }); 
	}
}

运行结果:


可以看到,Stream API的过滤方法也接受一个Predicate,这意味着可以将我们定制的 filter() 方法替换成写在里面的内联代码,这就是Lambda表达式的魔力。

5.Lambda表达式 vs 匿名类

        既然Lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对二者做一个比较分析。一个关键的不同点就是关键字 this。匿名类的 this 关键字指向匿名类,而lambda表达式的 this 关键字指向包围Lambda表达式的类。另一个不同点是二者的编译方式。Java编译器将Lambda表达式编译成类的私有方法。使用了Java 7的 invokedynamic 字节码指令来动态绑定这个方法。

6.Lambda表达式 vs 匿名类

  1. Lambda表达式仅能放入如下代码:预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为Lambda表达式的目标类型,可以用作返回类型,或Lambda目标代码的参数。例如,若一个方法接收Runnable、Comparable或者 Callable 接口,都有单个抽象方法,可以传入lambda表达式。类似的,如果一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么可以向其传lambda表达式。
  2. Lambda表达式内可以使用方法引用,仅当该方法不修改Lambda表达式提供的参数。Lambda表达式可以换为方法引用因为这仅是一个参数相同的简单方法调用。
    list.forEach(n -> System.out.println(n)); 
    list.forEach(System.out::println);  // 使用方法引用
    然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地Lambda表达式,如下所示:
    list.forEach((String s) -> System.out.println("*" + s + "*"));
    事实上,可以省略这里的Lambda参数的类型声明,编译器可以从列表的类属性推测出来。
  3. Lambda内部可以使用静态、非静态和局部变量,这称为Lambda内的变量捕获。
  4. Lambda表达式在Java中又称为闭包或匿名函数,所以如果有同事把它叫闭包的时候,不用惊讶。
  5. Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看Lambda表达式生成的字节码。
  6. Lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在Lambda内部修改定义在域外的变量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值