1.基本介绍
lambda表达式,即带有参数的表达式,为了更清晰地理解lambda表达式,先上代码:
1.1 两种方式的对比
1.1.1 方式1-匿名内部类
  1. class Student{
  2. private String name;
  3. private Double score;
  4.  
  5. public Student(String name, Double score) {
  6. this.name = name;
  7. this.score = score;
  8. }
  9.  
  10. public String getName() {
  11. return name;
  12. }
  13.  
  14. public Double getScore() {
  15. return score;
  16. }
  17.  
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21.  
  22. public void setScore(Double score) {
  23. this.score = score;
  24. }
  25.  
  26. @Override
  27. public String toString() {
  28. return "{"
  29. + "\"name\":\"" + name + "\""
  30. + ", \"score\":\"" + score + "\""
  31. + "}";
  32. }
  33. }:
  34. @Test
  35. public void test1(){
  36. List<Student> studentList = new ArrayList<Student>(){
  37. {
  38. add(new Student("stu1",100.0));
  39. add(new Student("stu2",97.0));
  40. add(new Student("stu3",96.0));
  41. add(new Student("stu4",95.0));
  42. }
  43. };
  44. Collections.sort(studentList, new Comparator<Student>() {
  45. @Override
  46. public int compare(Student o1, Student o2) {
  47. return Double.compare(o1.getScore(),o2.getScore());
  48. }
  49. });
  50. System.out.println(studentList);
  51. }

代码调用Collections.sort方法对集合进行排序,其中第二个参数是一个匿名内部类,sort方法调用内部类中的compare方法对list进行位置交换,因为java中的参数类型只能是类或者基本数据类型,所以虽然传入的是一个Comparator类,但是实际上可以理解成为了传递compare方法而不得不传递一个Comparator类 ,这种方式显得比较笨拙,而且大量使用的话代码严重冗余,这种情况在java8中通过使用lambda表达式来解决。

lambda表达式专门针对只有一个方法的接口(即函数式接口),Comparator就是一个函数式接口
  1. @FunctionalInterface
  2. public interface Comparator<T> {
  3. int compare(T o1, T o2);
  4. }
@FunctionalInterface的作用就是标识一个接口为函数式接口,此时Comparator里只能有一个抽象方法,由编译器进行判定。
使用lambda表达式之后方式1 中的代码改造如下
 
1.1.2 方式2-lambda表达式
  1. public void test1_(){
  2. List<Student> studentList = new ArrayList<Student>(){
  3. {
  4. add(new Student("stu1",100.0));
  5. add(new Student("stu2",97.0));
  6. add(new Student("stu3",96.0));
  7. add(new Student("stu4",95.0));
  8. }
  9. };
  10. Collections.sort(studentList,(s1,s2)-> Double.compare(s1.getScore(),s2.getScore()));
  11. System.out.println(studentList);
  12. }
1.2 lambda语法
1.2.1 多参数
     (1). lambda表达式的基本格式为(x1,x2)->{表达式...};
     (2). 在上式中,lambda表达式带有两个参数,此时参数类型可以省略,但两边的括号不能省略
     (3). 如果表达式只有一行,那么表达式两边的花括号可以省略
1.2.2 无参数
一个常见的例子是新建一个线程,不使用lambda表达式的写法为
  1. public void testThread(){
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. System.out.println("hello, i am thread!");
  6. }
  7. }).start();
  8. }
其中Runnable接口也是一个函数式接口,源码如下
  1. @FunctionalInterface
  2. public interface Runnable {
  3. /**
  4. * When an object implementing interface <code>Runnable</code> is used
  5. * to create a thread, starting the thread causes the object's
  6. * <code>run</code> method to be called in that separately executing
  7. * thread.
  8. * <p>
  9. * The general contract of the method <code>run</code> is that it may
  10. * take any action whatsoever.
  11. *
  12. * @see java.lang.Thread#run()
  13. */
  14. public abstract void run();
  15. }
将其转换为lambda表达式的写法为
  1. public void testThread_(){
  2. new Thread(()-> System.out.println("hello, i am thread!")).start();
  3. }
对于没有参数的情况 :
     (1).参数的括号不能省略,
     (2).其他语法同多参数
1.2.3 一个参数
我们构造一个只有一个参数的函数式接口
  1. @FunctionalInterface
  2. public interface MyFunctionalInterface {
  3. public void single(String msg);
  4. }
  5.  
  6. /**
  7. * 需要单个参数
  8. */
  9. public static void testOnePar(MyFunctionalInterface myFunctionalInterface){
  10. myFunctionalInterface.single("msg");
  11. }
  12. /**
  13. * 一个参数,可以省略参数的括号
  14. */
  15. @Test
  16. public void testOneParameter(){
  17. testOnePar(x-> System.out.println(x));
  18. }
对于一个参数的情况:
     (1).可以省略参数的括号和类型
     (2).其他语法同多参数
1.3 jdk提供的常用函数式接口
在这里我们为了演示只有一个参数的情况自己创建了一个函数式接口,其实java8中已经为我们提供了很多常见的函数式接口,截图如下:
常见的有
Function:提供任意一种类型的参数,返回另外一个任意类型返回值。 R apply(T t);
Consumer:提供任意一种类型的参数,返回空值。 void accept(T t);
Supplier:参数为空,得到任意一种类型的返回值。T get();
Predicate:提供任意一种类型的参数,返回boolean返回值。boolean test(T t);
因此针对上面的情况,我们可以直接使用Consumer类,
  1. /**
  2. * 需要单个参数
  3. */
  4. public static void testOnePar1(Consumer unaryOperator){
  5. unaryOperator.accept("msg");
  6. }
2.方法引用
lambda表达式用于替换函数式接口,方法引用也是如此,方法引用可以使代码更加简单和便捷
2.1 小试牛刀
上代码,根据List中字符串长度排序:
  1. public static void test1_() {
  2. List<String> strLst = new ArrayList<String>() {
  3. {
  4. add("adfkjsdkfjdskjfkds");
  5. add("asdfasdfafgfgf");
  6. add("public static void main");
  7. }
  8. };
  9. Collections.sort(strLst, String::compareToIgnoreCase);
  10. System.out.println(strLst);
  11. }
只要方法的参数和返回值类型与函数式接口中抽象方法的参数和返回值类型一致,就可以使用方法引用。
2.2 使用方式
方法引用主要有如下三种使用情况
     (1). 类::实例方法
     (2). 类::静态方法
     (3). 对象::实例方法
其中后两种情况等同于提供方法参数的lambda表达式,
如System.out::println 等同于(x)->System.out.println(x),
   Math::pow 等同于(x,y)->Math.pow(x,y).
第一种中,第一个参数会成为执行方法的对象,String::compareToIgnoreCase)等同于(x,y)->x.compareToIgnoreCase(y)
此外,方法引用还可以使用this::methodName及super::methodName表示该对象或者其父类对象中的方法
  1. class Father {
  2. public void greet() {
  3. System.out.println("Hello, i am function in father!");
  4. }
  5. }
  6.  
  7. class Child extends Father {
  8. @Override
  9. public void greet() {
  10. Runnable runnable = super::greet;
  11. new Thread(runnable).start();
  12. }
  13. }
  14. public static void main(String[] args){
  15. new Child().greet();
  16. }
最后打印的结果为:Hello, i am function in father!
3.构造器引用
构造器引用同方法引用类似,同样作用于函数式接口
构造器引用的语法为 ClassName::new
啥也不说,线上代码
  1. List<String> labels = Arrays.asList("aaa","bbb","ccc","ddd");
  2. Stream<Button> buttonStream = labels.stream().map(Button::new);
如上代码所示,map方法内需要一个Function对象
  1. <R> Stream<R> map(Function<? super T, ? extends R> mapper);
调用Button的构造器,接收一个String类型的参数,返回一个Button类型的对象
  1. public class Button extends ButtonBase {
  2.  
  3. /**
  4. * Creates a button with the specified text as its label.
  5. *
  6. * @param text A text string for its label.
  7. */
  8. public Button(String text) {
  9. super(text);
  10. initialize();
  11. }
  12. }
另外一个例子如下
  1. Button[] buttons1 = buttonStream.toArray(Button[]::new);
toArray方法的申明如下
  1. <A> A[] toArray(IntFunction<A[]> generator);
接收一个IntFunction类型的接口R apply(int value);该接口接收一个int型参数,返回指定类型
调用数组的初始化方法刚好适合。
有一个简单的构造器引用的例子如下:
  1. public class LambdaTest3 {
  2.  
  3. @Test
  4. public void test1_(){
  5. List<Integer> list = this.asList(ArrayList::new ,1,2,3,4,5);
  6. list.forEach(System.out::println);
  7. }
  8.  
  9. public <T> List<T> asList(MyCrator<List<T>> creator,T... a){
  10. List<T> list = creator.create();
  11. for (T t : a)
  12. list.add(t);
  13. return list;
  14. }
  15. }
  16. interface MyCrator<T extends List<?>>{
  17. T create();
  18. }
我们在项目中经常使用asList来创建一个ArrayList,但是也只能是ArrayList,
  1. public static <T> List<T> asList(T... a) {
  2. return new ArrayList<>(a);
  3. }
我们如何在asList中指定创建哪种类型的List的实例呢,使用构造器引用使得asList方法可以指定生成的List类型。
4.自由变量的作用范围
啥都不说,上代码先:
  1. public class LambdaTest4 {
  2. public void doWork1(){
  3. Runnable runnable = ()->{
  4. System.out.println(this.toString());
  5. System.out.println("lambda express run...");
  6. };
  7. new Thread(runnable).start();
  8. }
  9.  
  10. public void doWork2(){
  11. Runnable runnable = new Runnable() {
  12. @Override
  13. public void run() {
  14. System.out.println(this.toString());
  15. System.out.println("anony function run...");
  16. }
  17. };
  18. new Thread(runnable).start();
  19. }
  20. public static void main(String[] args) {
  21. new LambdaTest4().doWork1();
  22. new LambdaTest4().doWork2();
  23. }
  24. }
代码中doWork1和doWork2分别使用lambda表达式和匿名内部类的方式实现了Runnable接口,最后打印的结果如下
  1. com.java8.lambda.LambdaTest4@74f84cf
  2. lambda express run...
  3. com.java8.lambda.LambdaTest4$1@4295c176
  4. anony function run...
可见使用lambda表达式的方式,表达式中的this指的是包含lambda表达式的类,而使用匿名内部类的方式,this指的是匿名内部类本身。
4.1 自由变量和闭包
lambda达式中的变量有几类,1.参数内的变量,2.lambda表达式中的内部变量,3.自由变量,自由变量指的是在lambda表达式之外定义的变量。
包含自由变量的代码则称为闭包,如果理解了lambda表达式会在编译阶段被转换为匿名内部类,那么可以很容易理解自由变量在lambda表达式中的作用范围,在lambda表达式中会捕获所有的自由变量,并且将变量定义为final类型,所以不能改变lambda表达式中自由变量的值,如果改变,那么首先就无法编译通过。
对于引用类型(如ArrayList),final指的是引用指向的类始终不变,进行add操作是允许的,但是应该保证变量的线程安全。
代码如下所示:
  1. public class Outer {
  2. public AnnoInner getAnnoInner(int x) {
  3. int y = 100;
  4. return new AnnoInner() {
  5. int z = 100;
  6.  
  7. @Override
  8. public int add() {
  9. return x + y + z;
  10. }
  11. };
  12. }
  13.  
  14. public AnnoInner AnnoInnergetAnnoInner1(List<Integer> list1) {
  15. List<Integer> list2 = new ArrayList<>(Arrays.asList(1, 2, 3));
  16. return ()->{
  17. list2.add(123);
  18. int count = 0;
  19. Iterator<Integer> it = list1.iterator();
  20. while (it.hasNext()){
  21. count+=it.next();
  22. }
  23. Iterator<Integer> it1 = list2.iterator();
  24. while (it1.hasNext()){
  25. count+=it1.next();
  26. }
  27. return count;
  28. };
  29. }
  30.  
  31. @Test
  32. public void test(){
  33. AnnoInner res = new Outer().AnnoInnergetAnnoInner1(new ArrayList<>(Arrays.asList(1,2,3)));
  34. System.out.println(res.add());
  35. }
  36.  
  37. }
  38.  
  39. interface AnnoInner {
  40. int add();
  41. }
最后返回135
5.接口的静态方法和默认方法
java8对于接口做出了种种改进,使得我们可以在接口中实现默认方法和静态方法,见Comparator接口完整定义
  1. @FunctionalInterface
  2. public interface Comparator<T> {
  3.  
  4. int compare(T o1, T o2);
  5.  
  6. boolean equals(Object obj);
  7.  
  8. default Comparator<T> reversed() {
  9. return Collections.reverseOrder(this);
  10. }
  11.  
  12. default Comparator<T> thenComparing(Comparator<? super T> other) {
  13. Objects.requireNonNull(other);
  14. return (Comparator<T> & Serializable) (c1, c2) -> {
  15. int res = compare(c1, c2);
  16. return (res != 0) ? res : other.compare(c1, c2);
  17. };
  18. }
  19.  
  20. default <U> Comparator<T> thenComparing(
  21. Function<? super T, ? extends U> keyExtractor,
  22. Comparator<? super U> keyComparator)
  23. {
  24. return thenComparing(comparing(keyExtractor, keyComparator));
  25. }
  26.  
  27. default <U extends Comparable<? super U>> Comparator<T> thenComparing(
  28. Function<? super T, ? extends U> keyExtractor)
  29. {
  30. return thenComparing(comparing(keyExtractor));
  31. }
  32.  
  33. default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
  34. return thenComparing(comparingInt(keyExtractor));
  35. }
  36.  
  37. default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
  38. return thenComparing(comparingLong(keyExtractor));
  39. }
  40.  
  41. default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
  42. return thenComparing(comparingDouble(keyExtractor));
  43. }
  44.  
  45. public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
  46. return Collections.reverseOrder();
  47. }
  48.  
  49. @SuppressWarnings("unchecked")
  50. public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
  51. return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
  52. }
  53.  
  54. public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
  55. return new Comparators.NullComparator<>(true, comparator);
  56. }
  57.  
  58. public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
  59. return new Comparators.NullComparator<>(false, comparator);
  60. }
  61.  
  62. public static <T, U> Comparator<T> comparing(
  63. Function<? super T, ? extends U> keyExtractor,
  64. Comparator<? super U> keyComparator)
  65. {
  66. Objects.requireNonNull(keyExtractor);
  67. Objects.requireNonNull(keyComparator);
  68. return (Comparator<T> & Serializable)
  69. (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
  70. keyExtractor.apply(c2));
  71. }
  72.  
  73. public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
  74. Function<? super T, ? extends U> keyExtractor)
  75. {
  76. Objects.requireNonNull(keyExtractor);
  77. return (Comparator<T> & Serializable)
  78. (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
  79. }
  80.  
  81. public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
  82. Objects.requireNonNull(keyExtractor);
  83. return (Comparator<T> & Serializable)
  84. (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
  85. }
  86.  
  87. public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
  88. Objects.requireNonNull(keyExtractor);
  89. return (Comparator<T> & Serializable)
  90. (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
  91. }
  92.  
  93. public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
  94. Objects.requireNonNull(keyExtractor);
  95. return (Comparator<T> & Serializable)
  96. (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
  97. }
  98. }
在比较器接口中定义了若干用于比较和键提取的静态方法和默认方法,默认方法的使用使得方法引用更加方便,例如使用java.util.Objects类中的静态方法isNull和nonNull可以在Stream中很方便的进行null的判定(之后会有对于stream的介绍)。但是在接口中引入默认方法设计到一个问题,即
(1).接口中的默认方法和父类中方法的冲突问题
(2).接口之间引用的冲突问题
对于第一个冲突,java8规定类中的方法优先级要高于接口中的默认方法,所以接口中默认方法复写Object类中的方法是没有意义的,因为所有的接口都默认继承自Object类使得默认方法一定会被覆盖。
对于第二个冲突,java8强制要求子类必须复写接口中冲突的方法。如下所示:
  1. public class LambdaTest5 implements myInterface1, myInterface2 {
  2. @Override
  3. public void getName() {
  4. myInterface1.super.getName();
  5. }
  6.  
  7. public static void main(String[] args) {
  8. new LambdaTest5().getName();
  9. }
  10. }
  11.  
  12. interface myInterface1 {
  13. default void getName() {
  14. System.out.println("myInterface1 getName");
  15. }
  16.  
  17. ;
  18. }
  19.  
  20. interface myInterface2 {
  21. default void getName() {
  22. System.out.println("myInterface2 getName");
  23. }
  24. }
强制使用myInterface1中的getName方法
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

java8之lambda表达式入门的更多相关文章

  1. 函数式编程/lambda表达式入门

    函数式编程/lambda表达式入门 本篇主要讲解 lambda表达式的入门,涉及为什么使用函数式编程,以及jdk8提供的函数式接口 和 接口的默认方法 等等 1.什么是命令式编程 命令式编程就是我们去 ...

  2. Java Lambda表达式入门

    Java Lambda表达式入门 http://blog.csdn.net/renfufei/article/details/24600507 Java 8十个lambda表达式案例 http://w ...

  3. Java8中Lambda表达式的10个例子

    Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 //Before Java 8: new Thread(new Runnable() { @Overri ...

  4. java8的lambda表达式,将List<DTO> 转为 List<DO>

    将List<PhoneDTO>转为List<PhoneDO>,通过java8的lambda表达式来操作,比传统的for循环精简很多: /** * List<PhoneDT ...

  5. java8的lambda表达式

    关于java8的lambda表达式 lambda表达式一般用于接口,因为lambda表达式是函数式编程. 1.有且仅有一个抽象方法被称为函数式接口,函数式接口可以显示的被@FunctionalInte ...

  6. 30分钟入门Java8之lambda表达式

    前言 Google在今年发布Android N开发者预览版,一并宣布开始支持Java 8.我们终于能在Android开发中使用到Java8的一些语言特性了.目前支持: 默认方法 lambda表达式 多 ...

  7. 十分钟学会Java8的lambda表达式和Stream API

    01:前言一直在用JDK8 ,却从未用过Stream,为了对数组或集合进行一些排序.过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照. 刚开始写写是打基础,但写的多了,各种乏 ...

  8. Java8的lambda表达式和Stream API

    一直在用JDK8 ,却从未用过Stream,为了对数组或集合进行一些排序.过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照. 刚开始写写是打基础,但写的多了,各种乏味,非过来 ...

  9. Java8(一)--lambda表达式

    相信作为一个Java程序员都会或多或少的了解过Java8中的lambda表达式.函数式编程等,本人也是用过lambda表达式,使用的都是比较简单 的实现 通过一个例子去都感受lambda: Compa ...

随机推荐

  1. 【Android Developers Training】 12. 支持不同屏幕

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  2. html网页的兼容性和css优先级

    网页不仅是在一个浏览器上显示的网页,也要多考虑其他浏览器的兼容性,火狐.谷歌.搜狗等浏览器总体来说,网页的变化不大,最主要的是还是IE浏览器. color:red\9; IE6  IE7   IE8  ...

  3. C#调用TSC条码打印机打印二维码

    #region 调用TSC打印机打印 /// <summary> /// 调用TSC打印机打印 /// </summary> /// <param name=" ...

  4. gulp 运用 的理解

    ugulp.task('build', function() { runSequence('clean', 'copy', ['uglify', 'sass', 'htmlmin'], 'base64 ...

  5. Hibernate框架 初识 ORM概念 搭建Hibernate环境 Hibernate Api

    ORM概念 在学习 Hibernate 之前,我们先来了解ORM   对象关系映射 O, Object  对象 R,Realtion 关系  (关系型数据库: MySQL, Oracle…) M,Ma ...

  6. java 线程 理解 解析

    1 线程的概述 进程:正在运行的程序,负责了这个程序的内存分配,代表了内存中的执行区域. 线程:就是在一个进程中负者一个执行路径. 多线程:就是在一个进程中多个执行路径同时执行. 假象: 电脑上的程序 ...

  7. jquery.jconfirm兼容IE6

    因目标用户还在大量使用IE6(想吐CAO),只能做向下兼容,但之前使用的这个插件在IE6上并不支持.所以做了些处理才行. 以下为解决方法: IE6不支持position: fixed,所以需要对CSS ...

  8. (cljs/run-at (JSVM. :all) "Metadata就这样哦")

    前言  动态类型语言,少了静态类型语言必须声明变量类型的累赘,但也缺失了编译时类型检查和编译时优化的好处.cljs虽然作为动态类型语言,但其提供Metadata让我们在必要的时候可选择地补充类型提示, ...

  9. ES6 新增命令

    let               用来声明变量.它的用法类似于var,但是所声明的变量, 只在let命令所在的代码块内有效. 例: {var a=10; let b=20;}; console.lo ...

  10. Android - TabHost 与 Fragment 制作页面切换效果

    Android - TabHost 与 Fragment 制作页面切换效果 Android API 19 , API 23 三个标签页置于顶端 效果图: 在文件BoardTabHost.java中定义 ...