第一章 Lambda表达式

  Lamada 表达式是Java SE 8中最重要的新特性,长期以来被认为是在Java中缺失的特性,它的出现使整个java 语言变得完整。
至少到目前,在这节中你将学习到什么是lambda,为什么他是出色的一部分。同时也会介绍一些新的技术例如单一抽象方法和函数式接口。

  为什么是lambda 表达式。

  就像闭包一样, Lambda表达会使一些结构变得精简和良好的可读性,特别是在有处理内部类的时候。下面的例子是用内部类实现的代码块:

    Runnable runnable = new Runnable() {       

      @Override

      public void run() {            

        System.out.println("Running...");       

      }    

    }

   其实,这些代码可以用Lambda表达式替换成如下简短的代码:

     Runnable runnable = () -> System.out.println("Running...")

  换句话说, 如果你需要传递一个 runnable 接口给java.util.concurrent.Executor类,需要

    executor.execute(new Runnable() {

         @Override

      public void run() {

        System.out.println("Running...");

       }

     });

  但你可以用Lambda表达式实现同样的效果,用下面的代码;

     executor.execute(() -> System.out.println("Running..."));

  精短,优雅,干净,更富有表达性。

  修改接口。

  在深入研究lambda之前,先介绍一下在java 8 中接口里可以包含defalut 和 static 方法。这部分解释static和defalut 方法是什么,
  为什么这里需要大幅度的修改使lambda 融入到java 核心库中。

  默认方法

  将功能添加到接口是扩展接口一个安全的方式。不过,你最终会得到两个功能相似的接口。如果你只需要继承一个两个接口还是可以接受的,
  但如果需要添加新特性到成百的接口中,这就变成很严重的问题。

  这正是Java设计者们面对的问题,他们试图添加lambda 到java8 中去支持在集合框架中数十个接口。这些接口需要添加新的方法去支持Lambda,但是扩展所有的接口只会产出一倍的接口,其中一些接口可能会冠以List2或ExtendeSet这些丑陋的名字。

  反而,他们通过添加default方法解决了这个问题。 默认方法是在接口中已经实现好的方法。一个接口实现类不用实现默认方法,
这就意味着你 添加新的方法在接口中同时没有打破向后兼容性问题。

  在接口中定义默认方法,只需要在方法签名前加上“default”关键字即可。除此之外,添加大括号并在其中添加代码。

  在以后能学到,在java8中很多接口都有默认方法。

  当你实现带有默认方法的接口时,你有这些选择:
    忽略默认方法,直接继承他们就生效。
    重新声明默认方法,要改成抽象方法。
    重载默认方法。

  记住一点,java支持默认方法主要是为了向后兼容性。你绝不应该写程序不用类。

  静态方法。

  一个静态方法供类的所有实例共享,在java8以后,你可以在接口中使用静态方法 这样与接口相关联的所有静态方法可以用接口,而不是一个帮助类.

  静态方法的签名跟默认方法的签名很像,替代default关键字,使用static关键字,static在接口中默认是public的。

  静态方法在接口中很少见,在java.util package包中将近30个接口,只有两个有静态方法。

  函数式接口

    除了默认方法和静态方法,还有一个先决条件在学lambda之前:函数式接口。函数式接口只有一个抽象方法并且没有重写Object类中的方法,也被称作单一抽象方法(SAM),

  java.lang.Runnable就是一个函数式接口,它只有一个抽象方法 run(),函数式接口可以有默认方法和静态方法,复写了Object类中的public的方法也算是函数式接口。

  1. public interface Calculator {
  2.  
  3. double calculate(int a, int b);

  4. public default int subtract(int a, int b) {
  5. return a - b;
  6. }
  7.  
  8. public default int add(int a, int b) {
  9. return a * b;
  10. }

  11. @Override
  12. public String toString();
  13. }

  在Java核心库中常见的函数式接口有: java.lang.Runnable, java.lang.AutoCloseable, java.lang.Comparable,java.util.Comparator.此外,在java.util.function也有很多。

  函数式接口可以用注解@FunctionalInterface标注。

  为什么函数式接口这么重要?你可以使用函数式接口替代匿名内部类。如果不是,则不能这么用。

  lambda 表达式语法

  在上面的例子中calculate方法可以看作最基本的 lambda表达式语法。这个方法允许你定义任何的数学计算,使用两个整形参数返回一个double类型。下面有两个例子。

Calculator addition = (int a, int b) -> (a + b);
System.out.println(addition.calculate(5, 20)); // prints 25.0

Calculator division  = (int a, int b) -> (double) a / b;
System.out.println(division.calculate(5, 2)); // prints 2.5

Lambda表达式就是这么优雅,要是没有它你该多写多少代码?

Lambda表达式的标准定义有两种:

  1. (parameter list) -> expression

  1. (parameter list) -> {
  2. statements
  3. }
  4. 参数的类型跟接口中抽象方法的参数是一致的。不过,参数类型是可选的。换句话说,下面的方法是有效的。
    Calculator addition = (int a, int b) -> (a + b);
    Calculator addition = (a, b) -> (a + b);
    总结一下,Lambda表达式就是函数是接口实现的快捷方式,它相当于函数式接口实现的实例,因为在方法中可以使用Object作为参数,所以把Lambda表达式作为方法的参数也是可能的。
  5.  
  6. Java中预定义的函数式接口
    java.util.functionjava 8 中一个新包,里面包含了超过40个预定义函数式接口方便你去书写 Lambda 表达式风格的代码。下面是一些常用的接口。
  7.  
函数式接口 描述
Function 传递一个参数返回一个结果。这个结果的类型可以与参数的类型不相同。
BiFunction 传递两个参数返回一个结果。这个结果的类型可以与任一参数的类型不相同。
UnaryOperator 代表一个操作符的操作,它的返回结果类型与操作符的类型一样。实际上它可以被看作是Function 它的返回结果跟参数一样,它是Function 的子接口。
BiOperator 代表两个操作符的操作,它的返回结果类型必须与操作符相同。
Predicate 传递一个参数,基于参数的值返回boolean值。
Supplier 代表一个供应者的结果。
Consumer 传递一个参数但没有返回值。
  1.  
  2. Function, BiFunction and Other Variants
    Function接口通常用来创建一个参数的函数并返回一个结果。它是参数化的类型,定义如下:
    public interface Function<T, R>
    T是参数类型,R是返回的结果类型。
    Function只有一个抽象方法,apply,它的签名如下:
    R apply(T argument)
    这个方法需要重写当使用Function的时候,下面的例子定义了一个Function用来实现英米与公里的转换。Function传递一个Integer类型作为参数,并返回Double类型。

public class FunctionDemo1 {
    public static void main(String[] args) {
        Function<Integer, Double> milesToKms =
                (input) -> 1.6 * input;
        int miles = 3;
        double kms = milesToKms.apply(miles);
        System.out.printf("%d miles = %3.2f kilometers\n",
                miles, kms);
    }

}

  1.  
  2. 执行此类,在控制台输出:3 miles = 4.80 kilometers
  3.  
  4. BiFunctionFunction的一种变体,它传递两个参数并返回一个结果,下面的例子创建一个Function 根据给定的长和宽计算面积,
    需要调用apply方法实现。
import java.util.function.BiFunction;

public class BiFunctionDemo1 {
    public static void main(String[] args) {
        BiFunction<Float, Float, Float> area =
                (width, length) -> width * length;
        float width = 7.0F;
        float length = 10.0F;
        float result = area.apply(width, length);
        System.out.println(result);
    }
}

控制台输出:70.0
  1.  
  2. 除了BiFunction,还有一系列的Function变体,例如,IntFunction 只能传递int类型的参数。LongFunction and DoubleFunction IntFunction用法类似,只是传递的参数类型不同。
  3.  
  4. 还有一些变体不要求传递参数化的参数,因为他们被设计成传递制定类型参数,并返回制定类型的结果。例如,IntToDoubleFunction 接受int类型参数并返回double类型结果,还有区别是,使用
    applyAsDouble 替代apply方法,下面的例子实现摄氏温度转换成华氏温度。
import java.util.function.IntToDoubleFunction;

public class IntToDoubleFunctionDemo1 {
    public static void main(String[] args) {
        IntToDoubleFunction celciusToFahrenheit =
                (input) -> 1.8 * input + 32;
        int celcius = 100;
        double fahrenheit =
                celciusToFahrenheit.applyAsDouble(celcius);
        System.out.println(celcius + "\u2103" + " = "
                + fahrenheit + "\u2109\n");
    }
}

  1.  
  2. 控制台输出:100 = 212.0
  3.  
  4. LongToDoubleFunction and LongToIntFunction 用法与IntToDoubleFunction类似。
  5.  
  6. UnaryOperator Function 的另一种特殊实现,他的操作符类型与返回类型一样。它的定义如下:
      public interfaceUnaryOperator<T> extends Function<T,T>
    BinaryOperator BiFunction 的一特殊实现,要求所有参数类型一样并与返回类型一样。

Predicate

Predicate是根据传递参数的值返回boolean值。它有一个抽象方法test。

举例,下面的PredicateDemo1 类 定义一个Predicate 来判断传递进去的字符串每一个字符是否全是数字。

  1. import java.util.function.Predicate;
  2.  
  3. public class PredicateDemo1 {
  4. public static void main(String[] args) {
  5. Predicate<String> numbersOnly = (input) -> {
  6. for (int i = 0; i < input.length(); i++) {
  7. char c = input.charAt(i);
  8. if ("0123456789".indexOf(c) == -1) {
  9. return false;
  10. }
  11. }
  12. return true;
  13. };
  14.  
  15. System.out.println(numbersOnly.test("12345"));// true
  16. System.out.println(numbersOnly.test("100a")); // false
  17. }
  18. }

Supplier

Supplier没有参数但返回一个值。要实现这个接口必须重写get抽象方法并返回参数化类型的实例。

下面的例子,Supplier定义了一个返回 数字随机数,并使用for循环打印5个随机数。

  1. import java.security.SecureRandom;
  2. import java.util.function.Supplier;
  3.  
  4. public class SupplierDemo1 {
  5. public static void main(String[] args) {
  6. Supplier<Integer> oneDigitRandom = () -> {
  7. SecureRandom random = new SecureRandom();
  8. return random.nextInt(10);
  9. };
  10. for (int i = 0; i < 5; i++) {
  11. System.out.println(oneDigitRandom.get());
  12. }
  13. }
  14. }

还有一些Supplier的变体,例如DoubleSupplier (返回 Double), IntSupplier 和LongSupplier.

Consumer

Consumer是一个没有返回值的操作,它有一个抽象方法accept。

下面的例子使用ConSumer传递一个字符串参数,根据字符串的长度,和使用Function返回的空格的长度,最后把空格与字符串连接。

  1. import java.util.function.Consumer;
  2. import java.util.function.Function;
  3.  
  4. public class ConsumerDemo1 {
  5. public static void main(String[] args) {
  6. Function<Integer, String> spacer = (count) -> {
  7. StringBuilder sb = new StringBuilder(count);
  8. for (int i = 0; i < count; i++) {
  9. sb.append(" ");
  10. }
  11. return sb.toString();
  12. };
  13.  
  14. int lineLength = 60; // characters
  15. Consumer<String> printCentered = (input) -> {
  16. int length = input.length();
  17. String spaces = spacer.apply((lineLength - length) / 2);
  18. System.out.println(spaces + input);
  19. };
  20.  
  21. printCentered.accept("A lambda expression a day");
  22. printCentered.accept("makes you");
  23. printCentered.accept("look smarter");
  24. }
  25. }

Upgrading to Java 8——第一章 Lambda表达式的更多相关文章

  1. 第一章 Lambda表达式

    1.1 Why Lambdas? 当你操作多线程的时候,你会像下面这样将要处理的代码放到run()函数中: class Worker implements Runnable { public void ...

  2. Java核心技术-接口、lambda表达式与内部类

    本章将主要介绍: 接口技术:主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现一个或多个接口. lambda表达式:这是一种表示可以在将来的某个时间点执行的代码块的简洁方法. 内 ...

  3. Java函数式编程和lambda表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...

  4. 最全最强 Java 8 - 函数编程(lambda表达式)

    Java 8 - 函数编程(lambda表达式) 我们关心的是如何写出好代码,而不是符合函数编程风格的代码. @pdai Java 8 - 函数编程(lambda表达式) 简介 lambda表达式 分 ...

  5. Java 8:掌握 Lambda 表达式

    本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lambda 表达式的原理进行分析,最后对 Lambda 表达式的优缺点进行一个总 ...

  6. 第三章 Lambda表达式

    第三章 Lambda表达式 3.1 函数式编程思想概述 在数学中,函数就是有输入量.输出量的一套计算方案,也就是“拿什么东西做什么事情”.相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函 ...

  7. “全栈2019”Java多线程第一章:认识多线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. “全栈2019”Java异常第一章:什么是异常?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  9. Java基础教程:Lambda表达式

    Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...

随机推荐

  1. u-boot、kernel和filesystem 执行过程分析

    标题: Uboot -kerne-root 启动流程 内容: ※uboot启动流程 ※Kernel启动流程 ※Root启动流程 ※构建根文件系统 /************************** ...

  2. 在VMware中安装RHEL6.2(下)—— RHEL系统安装

    一. 打开安装好的虚拟机,因为上一篇我们未设置任何RHEL的安装源,所以它会如下图提示: 二. 图上标识为两种更改光盘设置的方法,物理或虚拟光盘皆可. 1. 选择①: 2. 选择②,点击设置...: ...

  3. POJ C++程序设计 编程题#1 编程作业—STL1

    编程题#1 来源: POJ (Coursera声明:在POJ上完成的习题将不会计入Coursera的最后成绩.) 注意: 总时间限制: 1000ms 内存限制: 65536kB 描述 下面的程序输出结 ...

  4. spark streaming kafka1.4.1中的低阶api createDirectStream使用总结

    转载:http://blog.csdn.net/ligt0610/article/details/47311771 由于目前每天需要从kafka中消费20亿条左右的消息,集群压力有点大,会导致job不 ...

  5. Android VideoView简单播放视频

    给Android VideoView一个文件目录,就可以直接播放智能设备中的视频文件,现在以播放事先用手机拍好并重命名的视频文件test.mp4为例.(1) 需要在布局文件中写一个ViedoView: ...

  6. ArrayAdapter的简单使用

    1.创建一个类继承ArrayAdapter private class MyAdapter extends ArrayAdapter { LayoutInflater in; Context cont ...

  7. 7-ZIP实现数据高度压缩

    From:http://www.cnblogs.com/killerlegend/p/3746395.html Author:KillerLegend Date:2013.5.22 选中文件,鼠标右键 ...

  8. mysql基本知识---20151127-2

    12.日期计算 YEAR( ).MONTH( )和DAYOFMONTH( ).CURDATE().RIGHT() 1>mysql> SELECT name, birth, CURDATE( ...

  9. @Async java 异步方法

    在spring 3中,@Async注解能让某个方法快速变为异步执行,马上来先DEMO上手下. 假如在网站的用户注册后,需要发送邮件,然后用户得到邮件确认后才能继续其他工作: 假设发送是一个很耗费时间的 ...

  10. EMVTag系列16《AC响应数据》

    在一个联机交易中,要传送到发卡行的专有应用数据. 字段 长度(字节) 赋值 说明 长度 1 07 分散密钥索引 1 00 密文版本号 1 01 根据发卡行密钥版本设置 卡片验证结果(CVR) 4 03 ...