Java 8 特性

作者: 李岩liyan | 来源:发表于2018-03-29 15:31 被阅读0次

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

    Lambda 表达式

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

    语法

    Lambda 表达式:

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

    字符串排序 pre Java 8 实现:

    List<String> strings = Arrays.asList("ab", "a", "bd", "bc", "c", "ca");
    Collections.sort(strings, new Comparator<String>() {
        @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<String> 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 <T> 是一个包含一个方法test(Object)的实用接口,这个方法返回一个布尔值,用来表示传入对象的测试结果是 true 或 false。

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

    测试一下:

    List<Integer> 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<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    List<String> 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<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
    List<Integer> 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<String> strings = Arrays.asList("123", "", "456", " ", "78", "", "9");
    List<String> 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<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
    

    统计计算

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

    List<Integer> 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<T> 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<Integer> a = Optional.ofNullable(value1);
            Optional<Integer> b = Optional.of(value2);
            System.out.println(optionalTest.sum(a,b));
        }
    
        private Integer sum(Optional<Integer> a, Optional<Integer> 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 特性

        本文链接:https://www.haomeiwen.com/subject/fnvmcftx.html