Java 8 实战 (一)

@[笔记, Java8, Java]

[TOC]

第一章 Java 8的更新

  1. 流处理

    流是一系列数据项,一次只生成一项。

  2. 通过 API来传递代码

  3. 并行与共享的可变数据

    对传给流方法的行为写法,必须能够同时对不同的输入安全地执行,这就意味着,代码不能访问共享的可变数据。


第二章 通过行为参数化传递代码

行为参数化,指让一个方法接受多种行为(或策略)作为参数,并在内部使用,来完成不同的行为。

处理行为参数化的问题,灵活性上定义接口实现类、使用匿名类、使用 Lambda 差不多。但是简洁程度上 Lambda 胜。


第三章 Lambda 表达式

Lambda 简介

Lambda 表达式为可传递的匿名函数的一种方式,它没有名称,但是它有参数列表、函数主题、返回类型,还有一个可以抛出的异常列表。

它可以被赋给一个变量,或者传递给一个接受函数式接口作为参数的方法。但是需要校验 参数列表返回类型 是否跟该变量、函数式接口需求的一样。

Java7的表达

Comparator<Apple> byWeight = new Comparator<Apple>(){
    public int compare(Apple a1, Apple a2){
        return a1.getWeight().compareTo(a2.getWeight());
    }
};

Lambda 表达式

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

Lambda的基本表达式为

(parameters)-> expression

(parameters) -> {statements;}

   (Integer i) -> return "Alan" + i; //无效
   (Integer i) -> { return "Alan" + i;} //有效
   
   (String s) -> {"Ironman";} // 无效
   (String s) -> "Ironman"; // 有效

怎样使用 Lambda

  1. 函数式接口

    简单的说,就是只定义了一个 抽象方法 的接口。因为现在接口有 默认方法,即在类没有对方法进行实现时,其主体为方法提供默认实现的方式。
    即使一个接口有很多的 默认方法 ,只要有一个 抽象方法,仍然是一个函数式接口。(继承来的抽象方法也认为是本接口的抽象方法。 )

    如:

    public interface Comparator<T>{
        int compare( T o1, To2 );
    }
    
    public interface Runnable{
        void run();
    }
    
  2. 函数描述符
    函数式接口的抽象方法的签名基本上就是需匹配的 Lambda 表达式的签名。我们将这种抽象方法叫做 函数描述符

  3. @FunctionalInterface 可以用来标记一个函数式接口,若本接口不是,编译器会报错。非强制。

Lambda 类型检查

  1. 类型检查过程

    a) 通过被传递的函数声明或者变量来找到类型
    b) 找到该类型的抽象方法 (故,一个 Lambda 表达式不能赋值给一个 Object 对象,因 Object 不是一个函数式接口)
    c) 找到该抽象方法的参数和返回值,进行类型检查。(对 void 类型有兼容。即一个有返回的 Lambda 表达式,可以赋值给一个返回 Void的函数式接口实例。)
    d) 若本 Lambda 表达式会抛出异常,需跟抽象方法的 throws 匹配。

  2. 类型推断

Comparator<Apple> b1 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

// b2同 b1的效果相同,编译器在这里会自动去匹配 a1和 a2的实际类型
Comparator<Apple> b2 = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
  1. 使用变量

a) Lambda 可以没有限制地使用实例变量和静态变量
b) Lambda 使用的局部变量,必须是 final 的,或者实际上 final 的。

究其本质原因,实际上就是静态变量和实例变量是在堆上的,而局部变量是在栈里的。
当拥有该变量的栈和 Lambda 所属于的栈为两个线程中时,会有线程安全问题。

方法引用

Lambda 表达式,是通过声明参数、方法体(针对参数列表的方法调用)、返回值的方式来声明一个方法。
而方法引用,就是避免参数调用其声明方法,而是直接指明类内方法的方式来创建 Lambda 表达式的方法。当需要使用一个方法引用时,目标引用放在分隔符前,方法名称放在后面。如:Apple::getWeight。(注意: 此处不应该带(),因为这里是一个对函数声明的引用,而不是对函数本身的调用。)

方法引用的普通类型

  1. 指向静态方法的方法引用, 如: Integer::parseInteger
  2. 指向实例方法的方法引用, 如: String::length
  3. 指向先用对象的实例方法的方法引用, 如:parameter::Function

构造方法引用

使用类名和关键字new的方法引用。
注意:方法引用是要赋值给一个函数式接口声明,同样需要进行返回值和类型检查。那么该接口声明的签名需跟当前类的某一个构造函数一直。
实例如下

// Supplier 的签名 () -> T
// 对应 Apple 的默认构造函数
// 等价于
// Supplier<Apple> c1 = () ->new Apple();
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();

// Function 的签名 (T) -> R
// 对应 Apple 的构造函数 Apple(int) 
// 等价于
// Function<Integer, Apple> c2 = (Integer weight) -> new Apple(weight);
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(100);

// BitFunction 的签名 (T, E) -> R
// 对应 Apple 的构造函数 Apple(String, int)
// 等价于
// BitFunction<String, Integer, Apple> c3 = (String color, Integer weight) -> new Apple( color, weight);
BitFunction<String, Integer, Apple> c3 = Apple::new;
Apple a3 = c3.apply("green", 100);

Lambda 带来的变化

以对一个 List 排序的例子,来汇总同样做 sort的几种做法。
void sort(Comparator<? super E> c)

  1. 代码传递
// 构造一个 Comparator 的实现类,并且实例化后传给 sort 方法。
public class AppleComparaotr implements Comparator<Apple>{
    public int compare(Apple a1, Apple a2 )
    {
        return a1.getWeight().compareTo(a2.getWeight()); 
    }
}

// 那么排序时
listInstance.sort( new AppleComparator() );
  1. 匿名类
listInstance.sort( new Comparator<Apple>{
        public int compare( Apple a1, Apple a2){
            return a1.getWeight().compareTo(a2.getWeight()); 
        }
    }
);
  1. Lambda
import static java.util.Comparator.comparing;
// 普通 Lambda
listInstance.sort( (Apple a1, Apple a2) ->  a1.getWeight().compareTo(a2.getWeight()) );

// 因为 Java 编译器有根据上下文判断 Lambda 参数类型的方法,那么实际上可简写为
listInstance.sort( (a1, a2) ->  a1.getWeight().compareTo(a2.getWeight()) );

// 使用现有接口 Comparator,可进一步简化为
listInstance.sort( Comparator.comparing( (Apple a) -> a.getWeight()) );
// 或者
listInstance.sort( Comparator.comparing( (a) -> a.getWeight()) )
  1. 方法引用
import static java.util.Comparator.comparing;

listInstance.sort( comparing(Apple::getWeight) );

复合 Lambda 表达式的有用方法

Java 8的几个接口(如 Comparator,Function, Predicate)都提供了将 Lambda 进行关联的方法。

  1. Compparator.reversed(), thenComparring();
  2. Predicate.negate(), Predicate.and(), Predicate.or();
  3. Function.andThen(), Function.compose();

第四章 流

流允许你以生命方式处理数据集合,通过查询语句来表达,而不是临时编写一个实现。它们可以被看做遍历数据集合的迭代器。此外,它们还可以透明地并行处理。

流可以进行筛选、切片、查找、匹配、映射、归约。

流与集合

流与集合的差别基本上就在于什么时候进行计算。
集合是内存中的一个数据结构,它包含了这个数据结构中的所有数据,每一个元素都是计算出结果后放入集合中。
流式一个固定的数据结构(不能增加或者删除),其元素都是按需计算的。就像是一个延迟创建的集合,只有消费者要求的时候才会计算值。

  1. 流跟迭代器一样,只能遍历一次。遍历完后,就说这个流已经被消费掉了。
Stream<Dish> s = menu.stream();

s.forEach(System.out::println); // 正常执行
s.forEach(System.out::println); // 报错 stream has already been operated upon or closed
  1. 对于集合的迭代操作,是外部迭代。而 Stream 类库使用内部迭代,开发者不需要关心迭代的具体实施方法,而只需要给出函数说明要做什么。
    外部迭代
List<String> names = new ArrayList<String>();

// 外部迭代一
for( Dish d : menu ){
    names.add(d.getName());
}

// 外部迭代二
Iterator<String> iterator = menu.iterator();
while( iterator.hasNext() )
{
    Dish d = iterator.next();
    names.add(d.getName());
}

内部迭代

List<String> names = menu.stream()
                    .map(Dish::getName)
                    .collect(toList());

流操作

  1. 中间操作

可以连接起来的流操作被称为中间操作,它们通常返回另外一个流。在碰到一个终端操作前,中间操作实际上并不会执行任何数据处理。

  1. 终端操作

关闭流的操作被称作终端操作,其返回结果可能是任何不是流的值。

第五章 使用流

筛选和切片


TODO

  • [ ]Scala 和 Groovy
  • [ ]访客模式
  • [ ]策略模式
  • [ ]构建器模式
  • [ ]Java FX API,一种现代的 Java UI 平台
  • [ ]Java7中的 try 语句,表示不需要显式的关闭资源。P41
  • [ ]Java7中的菱形表达式,用法。
  • [ ]java.util.Comparator.comparing
  • [ ]Guava, Apache Commons Collections, LambdaJ
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容