Java8 新特性与 lambda 表达式笔记

java8 的接口新特性

先看看下面的接口的默认方法和静态方法,感受一下:

public interface Java8Interface {
  //默认方法
  default String createName() {
    //逻辑实现
    return "xxxx";
  }
  //静态方法
  public static LocalDateTime getLocalDate() {
    return LocalDateTime.now();
  }
}
  1. 默认实现方法

如果多继承状态下有默认方法,遵循 3 条规则

  • 类胜与接口,如果在继承链中有方法体或抽象的方法声明,可以忽略接口中的定义

  • 子类胜于父类。如果一个接口继承了另外一个接口,而且两个接口都定义了相同的默认方法,那么子类中的默认方法胜出

  • 如上都不适用,要么子类实现该方法,要么将方法声明为抽象方法

  1. 静态方法

为了帮助编写类库的开发人员,Java 8 增加类接口静态方法新特性

lambda 表达式

函数式接口

函数式接口是 lambda 表达式的类型,其特点是只有一个抽象方法的接口,但是可以有 default 类型的方法实现。

JDK 提供几个常用的函数式接口

  1. Predicate<T>:通过传入一个对象T,返回是否匹配

@FunctionalInterface //这个注解是作用是为了标识该接口是函数式接口,编译时可以检查语法是否准确

public interface Predicate<T> {
  //唯一一个抽象方法
  boolean test(T t);
  //其他 default 的方法实现省略。。。
}
  1. Consumer<T>:传入对象 T,没有返回值,是一种消费型接口

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
  1. Function<T,R> :一个入参T,返回值 R

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
  1. Supplier<T> : 没入参,返回 T

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

lambda 表达式语法

(参数列表) -> {执行语句}

前面说了函数式接口是 lamdba 表达式的类型,所以其满足以下要求:

  1. lamdba 表达式的参数列表对应函数式接口方法的参数列表

  2. lambda 的返回类型对应函数式接口方法的返回类型

例如 JDK 提供的 Predicate<T> 接口来判断一个数是否大于 100:

Predicate<Integer> predicate = (Integer t) -> {return t > 100;};
System.out.println(predicate.test(300));//true

其中为了更加简洁,以下情况可以省略一些代码:

  1. 参数类型可省略
Predicate<Integer> predicate = (t) -> {return t > 100;};
  1. 入参只有一个,括号可省略
Predicate<Integer> predicate = t -> {return t > 100;};
  1. 如果表达式只有一条语句,大括号和 'return' 都可以省略
Predicate<Integer> predicate = t -> t > 100;

说明:都没有表明类型,程序是怎么知道的?其实是在编译的时候,通过前面指定的泛型类型来推断的,例如上面 Predicate<T> 中的 T 就是 Integer,进而推断出 t 是 Integer 类型。####Lambda 另外一个表达方式

方法引用

​ 如果 lambda 体中的内容已经有方法实现了,我们可以直接利用该方法代替。

语法如下:

使用规则:

  1. lamdba 体中调用方法的入参列表和返回值类型,要与函数接口抽象方法的一致!
  2. Lamdba 参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用类::实例方法名
  1. 对象:实例方法名
//1. 对象::实例方法名
@Test
public void methodRef() {
  Student student = new Student();
  //lambda 表达式方式
  Consumer<String> con = (x) -> student.setName(x);
  //方法引用方式
  Consumer<String> con2 = student::setName;
}
  1. 类:静态方法名
//2. 类::静态方法名
@Test
public void test2() {
  Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
  //方法引用方式
  Comparator<Integer> comparator2 = Integer::compare;
}
  1. 类:实例方法名
//3. 类::实例方法
//需要满足:Lamdba 参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时
public void test3() {
  BiPredicate<String, String> biPredicate = (x,y) -> x.equals(y);//x 为参数列表的第一个,并且是调用者,第二个参数y为方法的参数
  //方法引用方式
  BiPredicate<String, String> biPredicate2 = String::equals;
}
构造器引用
  1. 格式: Type::new
  2. 规则: 调用的构造器参数列表要与函数式接口抽象方法的参数列表保存一致

例如:

public void test4() {
  Supplier<Student> studentSupplier = () -> new Student();\
  //构造器引用:因为Supplier的抽象方法 get(),参数列表为空,所以调用Student的空参数构造器
  Supplier<Student> studentSupplier2 = Student::new;
}
数组引用

格式: Type[]::new


//数组引用
public void test5() {
  Function<Integer, String[]> function = (x) -> new String[x];
  //数组引用方式
  Function<Integer, String[]> function2 = String[]::new;
}

Stream

对比

举个栗子:过滤string 集合中包含 "sam" 的元素

以前的写法:

List<String> lists = new ArrayList<>();
List<String> result = new ArrayList<>();
for(String s : lists) {
  if (s.startsWith("sam")) {
    result.add(s);
  }
}

Stream 的写法:是不是很爽?

List<String> lists = new ArrayList<>();
List<String> result = lists.stream().filter(s -> s.startsWith("sam")).collect(Collectors.toList());

Stream 两种求值

一种是惰性求值,一种是及早求值。判断是标准是:如果返回值是Stream,那么就是惰性求值,否则是及早求值。只有遇到及早求值才会执行,否则不会执行。

  1. 只有惰性求值不会执行的:
List<Student> students = Arrays.asList(new Student("sam", 18, "男"));
Stream<String> stream = students.stream().map(student -> {
  System.out.println("name:" + student.getName());//没有输出
  return student.getName();
});
  1. 遇到及早求值 collect
    List<Student> students = Arrays.asList(new Student("sam", 18, "男"));
    long count = students.stream().map(student -> {
      System.out.println("name:" + student.getName());//输出
      return student.getName();
    }).count();

Stream 常用操作

  1. map: 输入 R 类型,返回 T 类型,用于类型转换。属于惰性求值
List<String> names = students.stream().map(student -> student.getName()).collect(Collectors.toList());
  1. collect: 从 stream 中生成一个集合,属于及早求值。一般结合 Collectors.xxx,如上面例子的Collectors.toList(),它会根据返回值的类型自动生成对应的类型。如果需要指定具体类型,可以通过 toCollection ,它接受一个函数作为参数,例如:
collect(Collectors.toCollection(TreeSet::new));//toCollection 参数是Supplier<C> 函数式接口\
  1. filter:过滤出满足条件的数据,也是 filter 表达式返回 true 的数据走到下一步操作。属于惰性求值
List<Student> students = students.stream().filter(student -> student.getName().startsWith("sam")).collect(Collectors.toList());
  1. flatMap: 基于 map 的功能,并且将返回多个 stream 连接成一个 stream。属于惰性求值
List<Student> students = Arrays.asList(new Student("sam", 18, "男"));
List<List<Student>> lists = Arrays.asList(students);
List<Student> allStudent = lists.stream().flatMap(students1 -> students1.stream()).collect(Collectors.toList());
//students1 -> students1.stream() : flatMap 将多个 stream 合成一个
  1. max 和 min: 求集合中最大值和最小值。属于及早求值
Student student1 = new Student("sam", 18, "男");
Student student2 = new Student("lilian", 28, "女");
List<Student> students = Arrays.asList(student1, student2);
//求年龄最大的学生
Optional<Student> max = students.stream().max(Comparator.comparing(Student::getAge));
System.out.println(max.get());//lilian
  1. reduce: 将一组的值经过操作(加减乘除等)生成一个值。reduce(a,b) 其中 a 表示上次的操作结果,b 表示下次操作的数,例如:
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4);
//计算集合中数字总和
Integer total = integerStream.reduce(0,(a, b) -> a + b);//0 表示初始值,可选
System.out.println(total);//10
reduce.png

Optional

Optional 是用来替换 null 值的,用于鼓励程序猿适当检查变量是否为空,并提供更多简便的操作 API.

常见用法
Optional<String> name = Optional.of("sam");
if (name.isPresent()) {//如果存在
  System.out.println(name.get());//取元素之前,如果不能确定是否一定有值,一定要判断是否存在
} else {
  //orElse 用来设置默认值
  String d = name.orElse("default");
  //orElseGet 用于备选值计算太繁琐而提供的
  String createDefault = name.orElseGet(() -> {
    return "sam";
  });
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容