Java 8 不止是Lambdas和Streams

转眼淘系应用升级JDK8已经几个月过去了,Lambdas表达式和Streams APIs的确给同学们带来了编程效率和代码可读性上的提升,代码变得更加简洁直接,更加符合人的思维(看来编程语言的发展也是本着“以人为本”的思路)。ATA上讲这两个新特性的文章已经很多了, 大鱼大肉大家吃的差不多,得常常家常小菜吧,下面总结一下常用的Java 8中Lambdas和Streams之外的一些新特性。

1. Default Method

Java 单继承的特性决定了一个类只能有1个父类,如果有通用的方法实现就只能写在父类里,如果是接口就不能有实现,偏偏Java 8中的stream方法就遇到了这个问题。原来没有吧,在Collection接口里加个非defaultstream方法,必须在所有实现接口的类中都要实现这个方法(难道他们真的把XXXListXXXSet都改了?)

肯(bu)定(yao)没(sha)有(le),Oracle猿们开了个后门(自己的东西就是方便,自己想改就改),他们用default method解决了这个问题。在Collection接口中实现了一个default method - stream, 这样所有其他的XXXList都不用改了,还可以直接使用这个方法。当然,只要实现类中不重写这个方法,实例调用的就是接口中的实现。

可以看到java.util.Collection中确实加了3个default方法。

default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
}

使用上其实就是default关键字,在interface的方法上加上default就会有2个作用:a. 接口中必须实现这个方法;b. 子类中可以不实现这个方法了。
另外,还可以在interface中加入static方法,配合default使用,这样直接静态方式使用接口的default方法,减少了写一些另外的util/helper类。

// 接口类
public interface BaseJava {

    default void sayHi(){
        System.out.println("Hi default method");
    }
    public void goodbye();
}

// 实现类
public class ChildJava implements BaseJava{

    @Override
    public void goodbye() {
        System.out.println("必须实现的方法");
    }

    public static void main(String...args){
        ChildJava cj = new ChildJava();
        cj.sayHi(); // default method
    }
}

2. Optional

业务应用的对象往往比较大,对象里面有对象,对象里面还有对象...在处理的时候,总是得判断是否为null,这无形增加了程序的判断深度(如果不加判断,就是NullPointerException了,关键是一旦出了异常,排查起来也是时间)

如果是这种普通的非空判断(用isPresent()代替!=null),其实和以前差不多,好像没有怎么方便(个人感觉甚至麻烦了,实际上相当于把非空判断封装了一个Helper静态方法)。

Optional<String> str = Optional.ofNullable("some returned value"); // 可能是空
System.out.println(str.isPresent()?str.get():"default value"); // Java 8
System.out.println(str.orElse("default value")); // Java 8
System.out.println(str!=null?str:"default value"); // Java 7

// 看看Java源代码
public boolean isPresent() {
        return value != null;
}
public T orElse(T other) {
        return value != null ? value : other;
}

但是,在对象比较复杂的情况下,就显得比较高效了,尤其是配合stream (reduce max min)和 lambdas表达使用。看到返回值是Optional的情况下,都会下意识去判断非空,这和之前相比,可以减少程序因为NullPointerException而出现的功能缺陷,提高开发效率(我记得google的一个三方包里早就有Optional这个东东了)。

3. 字符串API

Scenario 1. 业务应用处理最多的东西莫过于List和String,常见的操作就是给你一个List,处理一下串成一个String,或者给你一个String(用delimiter分隔的),处理一下变成一个List(木办法,前端要String ,后端要List,夹在中间只能不停地ForEach了)

这要搁以前(Java 7),某猿会这样写:

// 1 给前端拼一个逗号分隔的商品id列表的字符串
List<String> itemIdList = ...; // 从IC获取的热乎乎的List
StringBuffer sb = new StringBuffer();  // 也可以用StringBuilder...
for(String itemId: itemIdList){
        sb.append(itemId).append(","); // TODO  BUG 末尾还有一个 ,
}
return sb.toString();        

// 2 前端传来的都好分隔的商品id列表字符串,咱转成List给后端
String str ="1000,1001,1002,1003";
String[] arr=str.split(",");
List<String> list = Arrays.asList(arr);

这样写当然没问题,但是在业务逻辑处理中,老是来这么一下,一则影响代码美观(本来类像一棵直挺挺的树,加了几个这个,像是树瘤一样,100行代码中有50行是for循环...),二则容易打断了主要业务逻辑的思路(转而处理for循环里面的内容,当心最后一个 , )。

使用Java 8以后,情况会好很多,一两行代码搞定:

// 1 给前端拼一个逗号分隔的商品id列表的字符串
List<String> itemIdList = ...;
return String.join(",", itemIdList);

// 当然,如果获取的是Object,就需要配合streams来完成join了
List<ItemDO> itemDOs = ...
String names = itemDOs.stream().map(ItemDO::getItemId).collect(Collectors.joining(","));

// java 8 源码 String.join的实现
public static String join(CharSequence delimiter, CharSequence... elements) {
    Objects.requireNonNull(delimiter);
    Objects.requireNonNull(elements);
    // Number of elements not likely worth Arrays.stream overhead.
    StringJoiner joiner = new StringJoiner(delimiter);
    for (CharSequence cs: elements) {
        joiner.add(cs);
    }
    return joiner.toString();
}

4. 日期和时间API

Scenario 2. 业务应用总是免不了和日期时间打交道,而且,与多个系统对接后,要在日期、时间、日期时间、字符串等来回倒腾,前端要字符串,数据库存的datetime, 兄弟团队的二方包居然只要日期部分的String等等,当然还有日期的操作,向前多少天,向后多少天

Java 7当中,我们最长使用的莫过于Calendar了,因为它简单轻便,而且支持日期操作。此外,配合SimpleDateFormat, 就可以把任何String转换成日期,然后利用Calendar操作,最后再使用另一个SimpleDateFormat写成前端需要的格式。通常情况下,这样写:

SimpleDateFormat sdf =new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat sdf2 =new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2016/09/10");
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DAY_OF_MONTH, 10);
Date tenDaysLater = cal.getTime();
System.out.println(sdf2.format(tenDaysLater));

Java 8中引入了 LocalDateLocalTimeLocalDateTime 分别表示日期、时间、日期和时间,简单直接,而且就地提供了线程安全的日期时间操作,总体来说比7要方便很多了。上面的代码,在8里可以这样写:

LocalDate today = LocalDate.now();
System.out.println("today is " + today);

LocalDate specifiedDay = LocalDate.of(2016, Month.JUNE, 1);
System.out.println("specific date is " + specifiedDay);

LocalDate fifthIn2016 = LocalDate.ofYearDay(2016, 5);
System.out.println("5th day of 2016 is " + fifthIn2016);

// 默认使用 DateTimeFormatter.ISO_LOCAL_DATE
LocalDate parsedDay = LocalDate.parse("2016-08-31");
System.out.println("parsed day is " + parsedDay);

// 内置 yyyyMMdd
DateTimeFormatter basicIsoDate = DateTimeFormatter.BASIC_ISO_DATE;
// 内置 yyyy-MM-dd
DateTimeFormatter isoLocalDate = DateTimeFormatter.ISO_LOCAL_DATE;
// 自定义 yyyy:MM:dd
DateTimeFormatter customDate = DateTimeFormatter.ofPattern("yyyy:MM:dd");
// 转换
LocalDate formattedDay = LocalDate.parse("20160821", basicIsoDate);
System.out.println("isoLocalDate day is " + formattedDay.format(isoLocalDate));
System.out.println("customDate day is " + formattedDay.format(customDate));

// 时间操作
LocalTime now = LocalTime.now();
LocalTime later = now.plus(5, HOURS);

// 日期操作
LocalDate today = LocalDate.now();
LocalDate thirtyDaysLater = today.plusDays(30);
LocalDate afterOneMonth = today.plusMonths(1);
LocalDate beforeOneMonth = today.minusMonths(1);

相对于Calendar操作来说,LocalDate每次都返回一个全新的对象(LocalDate/LocalTime都是final class),这样做在多线程环境下更安全。

5. 总结

Java 8从应用升级到全面使用,大概还有比较长的路要走,毕竟很多已有的代码不会为了尝试新语法和特性而进行更新。在新的工程和代码中,尝试新的语法和特性,会带来更好的体验和效率。另外,功能等价的代码,除了语法上的不同,Java 7和Java 8在性能上有什么不同么,Java 8真的减少了资源提高了效率么?这个问题还得在今后的使用中进一步探讨。

6. 参考资料

Joda-Time 
Java 8 新特性
Java 8 基础实践

时间: 2024-12-14 18:48:06

Java 8 不止是Lambdas和Streams的相关文章

深入理解Java 8 Lambda(类库篇——Streams API,Collectors和并行)

转:http://zh.lucida.me/blog/java-8-lambdas-inside-out-library-features/ 关于 深入理解 Java 8 Lambda(语言篇--lambda,方法引用,目标类型和默认方法) 深入理解 Java 8 Lambda(类库篇--Streams API,Collector 和并行) 深入理解 Java 8 Lambda(原理篇--Java 编译器如何处理 lambda) 本文是深入理解 Java 8 Lambda 系列的第二篇,主要介绍

[转]深入理解Java 8 Lambda(类库篇——Streams API,Collectors和并行)

以下内容转自: 作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-library-features 本文谢绝转载,如需转载需征得作者本人同意,谢谢. -------------------------------------内容分割线--------------------------------------------------------- 深入理解Java

Java 8 Streams API 详解

流式编程作为Java 8的亮点之一,是继Java 5之后对集合的再一次升级,可以说Java 8几大特性中,Streams API 是作为Java 函数式的主角来设计的,夸张的说,有了Streams API之后,万物皆可一行代码. 什么是Stream Stream被翻译为流,它的工作过程像将一瓶水导入有很多过滤阀的管道一样,水每经过一个过滤阀,便被操作一次,比如过滤,转换等,最后管道的另外一头有一个容器负责接收剩下的水. 示意图如下: 首先通过source产生流,然后依次通过一些中间操作,比如过滤

Java Lambda表达式入门[转]

原文链接: Start Using Java Lambda Expressions http://blog.csdn.net/renfufei/article/details/24600507 下载示例程序 Examples.zip .原文日期: 2014年4月16日 翻译日期: 2014年4月27日翻译人员: 铁锚简介(译者注:虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能.本人建议不要

Java Lambda表达式入门

原文链接: Start Using Java Lambda Expressions 下载示例程序 Examples.zip . 原文日期: 2014年4月26日 翻译日期: 2014年4月27日 翻译人员: 铁锚 简介 (译者注:虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能.本人建议不要乱用,因为这就和某些很高级的黑客写的代码一样,简洁,难懂,难以调试,维护人员想骂娘.) Lambda表

java 8 中lambda表达式学习

转自 http://blog.csdn.net/renfufei/article/details/24600507 http://www.jdon.com/idea/java/10-example-of-lambda-expressions-in-java8.html Lambda表达式的语法基本语法:(parameters) -> expression或(parameters) ->{ statements; } 下面是Java lambda表达式的简单例子: // 1. 不需要参数,返回值

深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

最近看了一下java 8的一些新特性,其中最重要的莫过于Lambda表达式了,通过一阵子的学习,原本准备自己写一篇博客的,后来阅读了一位学长翻译过来的博客(原文是Brain Goetz的State of Lambda,下面会给出原文链接),觉得写的十分完美,把我想要写的和我没想到的都罗列了出来,就把这个分享给大家了. 注:原译文见  http://lucida.me/blog/java-8-lambdas-insideout-language-features/ 英语原版见:http://cr.

Java 8 特性 – 终极指南

第一次尝试翻译文章,有错误请见谅:) Java 8 特性 – 终极指南 (原文:http://www.javacodegeeks.com/2014/05/java-8-features-tutorial.html ) 编者注:Java 8出现在公众视野中已经有一段时间了,在这期间,种种迹象都表明Java 8是一个非常重要的版本. 我们已经在Java Code Geeks提供了很多丰富的教程,比方说Playing with Java 8 – Lambdas and Concurrency,Java

Java 终于有 Lambda 表达式啦~Java 8 语言变化&mdash;&mdash;Lambda 表达式和接口类更改【转载】

原文地址 en cn 下载 Demo Java? 8 包含一些重要的新的语言功能,为您提供了构建程序的更简单方式.Lambda 表达式 为内联代码块定义一种新语法,其灵活性与匿名内部类一样,但样板文件要少得多.接口更改使得接口可以添加到现有接口中,同时又不会破坏与现有代码的兼容性.本文将了解这些更改是如何协同工作的. Java 8 的最大变化在于添加了对 lambda 表达式 的支持.Lambda 表达式是可按引用传递的代码块.类似于一些其他编程语言中的闭包:它们是实现某项功能的代码,可接受一个