Java 8 特性

原文链接
非常简单地整理了一下Java 8 中出现的新特性,Lambda 部分的例子借鉴了这里 。

Lambda 表达式

Java 8 中最大特性,函数式编程

语法

Lambda 表达式:

parameter -> expression body
  1. 可选参数类型声明 - 不需要声明参数类型,编译器可以自动推断
  2. 可选小括号 - 只有一个参数时可以省略左侧的括号,多参数时小括号是必须的
  3. 可选大括号 - 当函数体只有一条语句时可以省略大括号
  4. 可选return关键字 - 函数体只有一条语句时,编译器会自动返回结果

字符串排序 pre Java 8 实现:

List strings = Arrays.asList("ab", "a", "bd", "bc", "c", "ca");
Collections.sort(strings, new Comparator() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});
System.out.println(strings);

out : [a, ab, bc, bd, c, ca]

如果用 Lambda 表达式:

List stringsLambda = Arrays.asList("ab", "a", "bd", "bc", "c", "ca");
Collections.sort(stringsLambda, (a, b) -> a.compareTo(b));
System.out.println(stringsLambda);

可选参数类型声明:(a, b) 等价于 (String a, String b)
可选小括号:此处有两个参数,不可省略
可选大括号、return关键字:a.compareTo(b) 等价于 {return a.compareTo(b);}

Lambda 范围

可以使用 final 类型的变量

public class Java8Tester {
    final static String salutation = "Hello! ";

    public static void main(String args[]) {
        GreetingService greetService1 = message -> System.out.println(salutation + message);
        greetService1.sayMessage("Mahesh");
    }

    interface GreetingService {
        void sayMessage(String message);
    }
}

Method references

Method references 可以通过名称指向方法,使用符号 ::,可以指向:

  1. 静态方法
  2. 实例方法
  3. 使用operator的构造函数 TreeSet::new
List names = new ArrayList();
names.add("Alice");
names.add("Bob");
names.add("Chirs");
names.forEach(System.out::println);

out:
Alice
Bob
Chris

Functional interfaces

Functional interfaces 具有单一功能,举例来说 Comparable 接口仅仅有一个用于比较大小的方法CompareTo , Java 8 中定义了很多 Functional interfaces,以便在 lambda表达式 中广泛使用。

Predicate 是一个包含一个方法test(Object)的实用接口,这个方法返回一个布尔值,用来表示传入对象的测试结果是 true 或 false。

public static void eval(List list, Predicate predicate) {
    for(Integer n: list) {
        if(predicate.test(n)) {
            System.out.print(n + " ");
        }
    }
    System.out.println();
}

测试一下:

List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println("Print all numbers:");
eval(list, n->true);
System.out.println("Print even numbers:");
eval(list, n-> n%2 == 0 );
System.out.println("Print numbers greater than 3:");
eval(list, n-> n > 3 );

out :
Print all numbers:
1 2 3 4 5 6 7 8 9
Print even numbers:
2 4 6 8
Print numbers greater than 3:
4 5 6 7 8 9

Default Methods

Default Methods,可以让原有接口使用 Lambda 表达式。
比如,ListCollection接口都没有声明 forEach方法,强行加上这类方法又会破坏集合原有的实现框架。

语法

public interface vehicle {
   default void print() {
      System.out.println("I am a vehicle!");
   }
}

Multiple Defaults

当接口中有 default 函数时,有可能一个类实现了2个接口,而这2个接口中包含了同名的 default 函数

public interface vehicle {
   default void print() {
      System.out.println("I am a vehicle!");
   }
}

public interface fourWheeler {
   default void print() {
      System.out.println("I am a four wheeler!");
   }
}

第一种解决办法:方法重写

public class car implements vehicle, fourWheeler {
   default void print() {
      System.out.println("I am a four wheeler car vehicle!");
   }
}

第二种解决办法:通过super调用特定接口的default方法

public class car implements vehicle, fourWheeler {
   default void print() {
      vehicle.super.print();
   }
}

Static Default Methods

接口同时可以拥有静态函数

public interface vehicle {
   default void print() {
      System.out.println("I am a vehicle!");
   }

   static void blowHorn() {
      System.out.println("Blowing horn!!!");
   }
}

Stream

Stream 是一个抽象层的东西,使用Stream可以像SQL语句那样操作数据(Stream 并不能处理SQL语句):

SELECT max(salary), employee_id FROM Employee

上面的SQL语句可以直接返回最高工资员工的工资和ID,而使用 Collections 集合框架,则必须遍历和多次check才能查找出上面的信息。
Stream 的另一个好处就是高效,为避免并发下容易产生的错误,Java 8 中引入 Stream 的概念,让我们可以直接使用多核系统而不用考虑具体的代码实现。

Stream 表示一系列来自Source的对象

  1. 元素序列 - Stream 提供一个元素的集合,其中的元素连续排列,且类型确定。
  2. 源Source - Stream 可以把集合类、Arrays、I/O源当做源。
  3. Aggregate operations - Stream 支持集合运算:filter、map、limit、reduce、find、match等等
  4. Pipelining - 多数 stream 操作返回 stream 本身,所以其操作结果可以用流水线的方式处理。这些操作可以叫 intermediate operations,它们接收输入、处理、并将结果返回。collect() 操作是一个末端操作,通常在 pipelining 操作结束时出现以标记流的结束。
  5. 自动迭代 - 在源元素内部遍历,与需要显示迭代的集合类不同。

生成 Stream

Collection 接口有两个方法生成 Stream

  • stream() 返回一个将当前集合内容作为源的 sequential stream
  • parallelStream() 返回一个将当前集合内容作为源的 parallel Stream

Collectors

一个 Stream 经过流操作之后还是 Stream 对象,可以通过 Collectors 将流处理的结果转换为ListString。下面例子中生成Stream对象,没有做任何流操作,再将其转换为List,故filteredstrings内容一致。

List strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List filtered = strings.stream().collect(Collectors.toList());

另外,Collectors也可以将不同流操作的结果合并起来。

String mergedString = strings.stream().collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
//out : Merged String: abc, , bc, efg, abcd, , jkl

map

把每一个元素映射为相应的结果。下面例子中将数字映射为其平方,并打印不重复的值

List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List squaresList = numbers.stream()
    .map( i -> i*i)
    .distinct()
    .collect(Collectors.toList());
System.out.println(squaresList); // [9, 4, 49, 25]

filter

滤除不符合条件 (保留符合条件)的元素,filter(paramters -> condition)

limit

限制Stream的大小,limit(5) 流中元素不够5个时不报错,取当前所有元素。

sorted

排序

将滤除空字符串,取前5个元素,并排序:

List strings = Arrays.asList("123", "", "456", " ", "78", "", "9");
List result = strings.stream()
    .filter(str -> !str.isEmpty())
    .limit(5)
    .sorted()
    .collect(Collectors.toList());
System.out.println(result);   //[ , 123, 456, 78, 9]

forEach

Stream 提供了遍历元素的新方法forEach,通过它打印10个随机数:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

random.ints() 会返回一个 Stream对象

并行处理

做并行处理时可选用 parallelStream

List strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();

统计计算

流处理结束后,可以计算统计数据

List integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());

out:
Highest number in List : 19
Lowest number in List : 1
Sum of all numbers : 85
Average of all numbers : 9.444444444444445

Optional Class

Optional 是一个包含非null对象的容器, Optional 对象用值的缺失来表示null。
Optional 类有很多实用方法,可以将一个值处理为可用或不可用状态,从而不用进行是否为空值的检查。类似于Guava中的Optional。

java.util.Optional 定义如下:

public final class Optional extends Object

案例:

import java.util.Optional;

public class OptionalTest {
    public static void main(String[] args){
        OptionalTest optionalTest = new OptionalTest();
        Integer value1 = null;
        Integer value2 = new Integer(10);
        Optional a = Optional.ofNullable(value1);
        Optional b = Optional.of(value2);
        System.out.println(optionalTest.sum(a,b));
    }

    private Integer sum(Optional a, Optional b) {
        System.out.println("First parameter is present: " + a.isPresent());
        System.out.println("Second parameter is present: " + b.isPresent());
        Integer value1 = a.orElse(new Integer(0));
        Integer value2 = b.get();
        return value1 + value2;
    }
}

out:
First parameter is present: false
Second parameter is present: true
10

新的 Date/Time API

原有时间API

  1. 线程不安全
    java.util.Date 线程不安全,新的时间API是不可变的,且没有setter方法
  2. 设计糟糕
    原有时间API起始1900,月份从1开始,日期从0开始,没有一致性。新API中提供了很多实用的方法。
  3. 时区处理复杂

java.time

  • Local 简化的 date-time API
  • Zoned 专门处理多时区的 date-time API

LocalDateTimeLocalDateLocalTime

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
System.out.println(localDateTime.getDayOfMonth());

LocalDate localDate = localDateTime.toLocalDate().withMonth(2);
System.out.println(localDate);

LocalDate date = LocalDate.of(1978, Month.SEPTEMBER, 21);
System.out.println(date);
LocalTime time = LocalTime.of(22, 15);
System.out.println(time);

LocalTime time2 = LocalTime.parse("20:15:30");

更多内容详见 [更多内容详见 Java8 Tutorial 。

你可能感兴趣的:(Java 8 特性)