粗略的概括:lambda表达式主要用来实现“函数接口”中“唯一”的抽象方法用的。

      他的特殊版有 方法引用,构造函数引用,用对应的接口实例接收即可。

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式 它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

  • 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
  • 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样, Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
  • 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
  • 简洁——无需像匿名类那样写很多模板代码。

lambda表达式的5种方式

public void process(Runnable r){
r.run();
}
process(() -> System.out.println("This is awesome!!"));

为什么只有在需要函数式接口的时候才可以传递Lambda呢?

函数式接口

函数式接口就是只定义一个抽象方法的接口;哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

一般来说最好用@FunctionalInterface 注解标注一下。

在哪里可以使用Lambda

第三个例子的Predicate接口的定义如下:

public interface Predicate<T>{
boolean test (T t);
}

 环绕模式没看懂

通用函数式接口

Predicate、 Consumer和Function

  1. java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。

    Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
    List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
  2. java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void) 。
  3. java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。

泛型的一个坑

1.Java类型要么是引用类型(比如Byte、 Integer、 Object、 List) ,要么是原始类型(比如int、 double、 byte、 char)。但是泛型(比如Consumer<T>中的T)只能绑定到引用类型。

2.原始类型也是可以使用的,但是会在内部被封装为引用类型---作者强调---

这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。 

所以针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,接口如下:

总结关于函数式接口和Lambda的使用案例、 Lambda的例子,以及可以使用的函数式接口。

Lambda,还有函数式接口 的异常处理

@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;//接口中抛出异常
}
//接口无法抛出的,try catch 包裹
Function<BufferedReader, String> f = (BufferedReader b) -> {
  try {
    return b.readLine();
   }
  catch(IOException e) {
    throw new RuntimeException(e);
   }
};

 困惑

当我们第一次提到Lambda表达式时,说它可以为函数式接口生成一个实例。然而, Lambda表达式本身并不包含它在实现哪个函数式接口的信息。为了全面了解Lambda表达式,你应该知道Lambda的实际类型是什么。

类型检查、类型推断以及限制 

有了目标类型的概念,同一个Lambda表达式就可以与不同的函数式接口联系起来,只要它们的抽象方法签名能够兼容。比如,前面提到的Callable和PrivilegedAction,这两个接口都代表着什么也不接受且返回一个泛型T的函数。 因此,下面的赋值是有效的: 

Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42; Comparator<Apple> c1 =
   (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
ToIntBiFunction<Apple, Apple> c2 =
  (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
BiFunction<Apple, Apple, Integer> c3 =
  (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

所以需要从赋值的上下文、方法调用的上下文(参数和返回值) 来判断lambda表达式。

类型推断

Comparator<Apple> c =
  (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Comparator<Apple> c =
  (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
//有时候显式写出类型更易读,有时候去掉它们更易读。 这两种都可以编译。
List<Apple> greenApples =
  filter(inventory, a -> "green".equals(a.getColor()));
//当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略。

局部变量的限制(懵懂)

下面的代码无法编译
错误: Lambda表达式引用的局部变量必须是最终的( final)或事实上最终的

int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;

原书中是这样描述的

方法引用

方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。

当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面。例如,Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()的快捷写法。

方法引用主要有三类。

  1.指向静态方法的方法引用

    (例如Integer的parseInt方法,写作Integer::parseInt)

  2.指 向 任 意 类 型 实 例 方 法 的 方 法 引 用

    ( 例 如 String 的 length 方 法 , 写 作String::length)

  3.指向现有对象的实例方法的方法引用

  (假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)。

将lambda表达式重构为等价的方法引用

List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
//等价与
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);

 构造函数引用

如果就new 一个对象感觉这样有点啰嗦,如果new多个对象,确实省点代码。

构造函数引用的例子:

---------------------------------------------------------------------------------------------------------------------------------

看个例子:

需求--用不同的排序策略给一个Apple列表排序

//第一版正常java代码:
public class AppleComparator implements Comparator<Apple> {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator()); //inventory是一个List的实例,下同 //第二版 匿名内部类
inventory.sort(new Comparator<Apple>() {
  public int compare(Apple a1, Apple a2){
    return a1.getWeight().compareTo(a2.getWeight());
  }
});

//第三版 lambda表达式
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
或者
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
或者
Comparator具有一个叫作comparing的静态辅助方法,它可以接受一个Function来提取Comparable键值,并生成一个Comparator对象(接口的静态方法)
Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());
现在你可以把代码再改得紧凑一点了:
import static java.util.Comparator.comparing;
inventory.sort(comparing((a) -> a.getWeight())); //第四版(最终版)
import static java.util.Comparator.comparing;
inventory.sort(comparing(Apple::getWeight));

---------------------------------------------------------------------------------------------------------------------------------

复合Lambda表达式

比较器复合 --Comparator.comparing 的内置方法

inventory.sort(comparing(Apple::getWeight) //comparing reversed thenComparing 方法返回仍然是Comparator所以可以继续调用内置方法 
  .reversed()
  .thenComparing(Apple::getCountry));

谓词复合 --Predicate 接口自带的 and or negate isEqual方法

Predicate<Apple> redAndHeavyAppleOrGreen =
redApple.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));

函数复合 --Function接口的andThen和compose两个默认方法 

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
//先加1在乘2
Function<Integer, Integer> h = f.andThen(g);  数学上会写作g(f(x))
//先乘2在加1
Function<Integer, Integer> h = f.compose(g); 数学上会写作f(g(x))
int result = h.apply(1);

 

java 8 学习二(Lambda表达式)的更多相关文章

  1. Java 8学习之Lambda表达式

    一.lambda表达式 一个lambda表达式包含三个部分: 一段代码 参数 自由变量的值,这里的自由指的是哪些不是参数并且没有在代码中定义的变量. 示例: public static void re ...

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

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

  3. Java8学习笔记----Lambda表达式 (转)

    Java8学习笔记----Lambda表达式 天锦 2014-03-24 16:43:30 发表于:ATA之家       本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人 ...

  4. Lambda(二)lambda表达式使用

    Lambda(二)lambda表达式使用 Lambda 表达式组成: /* param list arrow lambda body (o1,o2) -> o1.getColor().Compa ...

  5. JAVA8学习——深入浅出Lambda表达式(学习过程)

    JAVA8学习--深入浅出Lambda表达式(学习过程) lambda表达式: 我们为什么要用lambda表达式 在JAVA中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法. 在 ...

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

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

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

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

  8. java函数式编程之lambda表达式

    作为比较老牌的面向对象的编程语言java,在对函数式编程的支持上一直不温不火. 认为面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:如果你想写一个方法,那么就必须把它放到一个类里面,然后n ...

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

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

  10. Java 8:掌握 Lambda 表达式

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

随机推荐

  1. Spring AOP环绕异常影响的报错

    最近遇到一个问题,异常是: java.lang.ClassCastException: org.springframework.http.ResponseEntity cannot be cast t ...

  2. rocketmq常用命令整理

    1. 启动namesrv和borker sh /opt/alibaba-rocketmq/bin/runserver.sh com.alibaba.rocketmq.namesrv.NamesrvSt ...

  3. 出师表(ENGLISH) 强烈打call啊~王洛勇是什么神仙英语

    臣亮言:先帝创业未半而中道崩殂, Permit me to observe: the late emperor was taken from us before he could finish his ...

  4. 通过IP获取MAC地址例子(内核层)

    博客地址:http://home.cnblogs.com/u/zengjianrong/ 在内核处理此流程,反而更加简单些,代码如下: #include <net/arp.h> #incl ...

  5. 【生活现场】从打牌到map-reduce工作原理解析(转)

    原文:http://www.sohu.com/a/287135829_818692 小史是一个非科班的程序员,虽然学的是电子专业,但是通过自己的努力成功通过了面试,现在要开始迎接新生活了. 对小史面试 ...

  6. 剑指offer 栈的压入和弹出

    题目描述输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈 ...

  7. linux系统shell基础知识入门二

    条件判断语句 test或[],这两是等价的.但用[]这种可能看起来更简洁 必须在[符号和检查条件之间留出空格,而test命令之后也总是应该有一个空格 如果要把test 和then 放一行上,那么必须在 ...

  8. PHP错误日志相关

    https://cloud.tencent.com/developer/article/1167951   php错误日志总结 https://cloud.tencent.com/developer/ ...

  9. nginx rewrite模块

    return 从0.8.42版本开始, return 语句可以指定重定向 url (状态码可以为如下几种 301,302,303,307), 也可以为其他状态码指定响应的文本内容,并且重定向的url和 ...

  10. Spring常用注解之一

    Spring中的常用注解 @Component 把普通 pojo 实例化到 Spring 容器中,相当于配置文件中的 泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller. ...