奔跑吧,JAVA!

JAVA8 新特性~~~~

2018-03-11  本文已影响27人  BAHUANNN

Lambda 表达式&函数式接口

Lambda表达式好像已经出现很久了好像大部分人还是比较倾向于使用内部类
(老程序员表示Lambda表达式影响可读性,但是在很多组件中使用Lambda表达式还是很有必要的)

Lamdba表达式和接口是分不开的,JAVA8改动Lambda表达式的同时也新增了函数式接口

函数式接口

Functional Interface的定义很简单:任何包含唯一一个抽象方法的接口都可以称之为函数式接口。

但是函数式接口中还可以存在签名与Object的public方法相同的接口(毕竟最终父类时Object),并且可以存在静态方法。

@FunctionalInterface
interface FunctionalInterfaceWithStaticMethod {
    static int sum(int[] array) {
        return Arrays.stream(array).reduce((a, b) -> a+b).getAsInt();
    }

    boolean equals(Object obj);
    
    void apply();
}
//这依旧是一个函数式接口

为了让编译器帮助我们确保一个接口满足函数式接口的要求,Java8提供了@FunctionalInterface注解。

另外JDK中已有的一些接口本身就是函数式接口,如Runnable。 JDK 8中又增加了java.util.function包, 提供了常用的函数式接口。

举个函数式接口的栗子

@FunctionalInterface  
public interface Runnable {  
    public abstract void run();  
}  
//Runnable接口提供了一个run()抽象方法

在java8之前,你可以通过匿名内部类来实现对这个接口的调用,像下面这样

Thread thread = new Thread(new Runnable() {
  public void run() {
    System.out.println("In another thread");
  }
}); 

但是如果我们想要我们的代码更加优雅,我们应该使用Lambda表达式,当我们使用Lambda表达式刚刚代码就可以这样写:

Thread thread = new Thread(() -> 
System.out.println("In another thread"));

Lambda表达式

Lambda表达式在Java8中最大的改动是
它允许把函数作为一个方法的参数(函数作为参数传递进方法中)

以下是lambda表达式的重要特征:

还是先上栗子:

public class Java8Tester {
   public static void main(String args[]){
      Java8Tester tester = new Java8Tester();
        
      // 类型声明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用类型声明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括号中的返回语句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 没有大括号及返回语句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括号
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括号
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }
}

变量作用域

int num = 1;
Converter<Integer, String> s =
        (param) -> String.valueOf(param + num);
num = 5;
//编译会出错
String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); 
 //编译会出错 

方法引用

方法引用通过方法的名字来指向一个方法。
方法引用使用一对冒号 ::
内容比较简单并且Java核心已经有讲过
所以直接上栗子,我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。

package com.runoob.main;
 
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
 
class Car {

    public static Car create(final Supplier<Car> supplier) {
        return supplier.get();
    }
 
    public static void collide(final Car car) {
        System.out.println("Collided " + car.toString());
    }
 
    public void follow(final Car another) {
        System.out.println("Following the " + another.toString());
    }
 
    public void repair() {
        System.out.println("Repaired " + this.toString());
    }
}
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
cars.forEach( Car::collide );
//Class< T >::new
cars.forEach( Car::repair );
//Class::method
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
//instance::method

Java Stream

Stream是 Java 8新增加的类,用来补充集合类
既然是补充集合类,那么先来列举一下它们的区别(主要是与迭代器的区别):
1.不储存数据 流是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。
2.函数式编程 流的操作不会修改数据源,例如filter不会将数据源中的数据删除。
3.延迟操作 流的很多操作如filter,map等中间操作是延迟执行的,只有到终点操作才会将操作顺序执行。
4.可以解绑 对于无限数量的流,有些操作是可以在有限的时间完成的,比如limit(n) 或 findFirst(),这些操作可是实现"短路"(Short-circuiting),访问到有限的元素后就可以返回。
5.纯消费 流的元素只能访问一次,类似Iterator,操作没有回头路,如果你想从头重新访问流的元素,对不起,你得重新生成一个新的流。

在具体讲解方法之前,还是先来看一个栗子:

List<Integer> a = {1,2,3};
List<Integer> b = a.stream()
             .filter(i ->  i >= 2 )
             .collect(Collectors.toList());

我们可以把以上代码分为三部分来具体分析:
1.创建Stream

2.中间操作

List<String> l = Stream.of("a","b","c","b")
        .distinct()
        .collect(Collectors.toList());
System.out.println(l); //[a, b, c]
List<Integer> l = IntStream.range(1,10)
        .filter( i -> i % 2 == 0)
        .boxed()
        .collect(Collectors.toList());
System.out.println(l); //[2, 4, 6, 8]
map( c -> c*2)
String poetry = "Where, before me, are the ages that have gone?\n" +
        "And where, behind me, are the coming generations?\n" +
        "I think of heaven and earth, without limit, without end,\n" +
        "And I am all alone and my tears fall down.";
Stream<String> lines = Arrays.stream(poetry.split("\n"));
Stream<String> words = lines.flatMap(line -> Arrays.stream(line.split(" ")));

List<Integer> l = IntStream.range(1,100).limit(5)
        .boxed()
        .collect(Collectors.toList());
System.out.println(l);//[1, 2, 3, 4, 5]
String[] arr = new String[]{"a","b","c","d"};
Arrays.stream(arr)
        .peek(System.out::println) //a,b,c,d
        .count();

对于有序流,排序是稳定的。对于非有序流,不保证排序稳定。

String[] arr = new String[]{"b_123","c+342","b#632","d_123"};
List<String> l  = Arrays.stream(arr)
        .sorted((s1,s2) -> {
            if (s1.charAt(0) == s2.charAt(0))
                return s1.substring(2).compareTo(s2.substring(2));
            else
                return s1.charAt(0) - s2.charAt(0);
        })
        .collect(Collectors.toList());
System.out.println(l); //[b_123, b#632, c+342, d_123]

3.终点操作

public boolean  allMatch(Predicate<? super T> predicate)
public boolean  anyMatch(Predicate<? super T> predicate)
public boolean  noneMatch(Predicate<? super T> predicate)

Stream.of(1,2,3,4,5).forEach(System.out::println);

多说一句~~~~~~~~~
流可以从非线程安全的集合中创建,当流的管道执行的时候,非concurrent数据源不应该被改变。下面的代码会抛出java.util.ConcurrentModificationException异常:

List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
sl.forEach(s -> l.add("three"));

但是使用CopyOnWriteArrayList可以解决这个问题

Optional——一个可以为 null 的容器

Java 8中的Optional<T>是一个可以包含或不可以包含非空值的容器对象,在 Stream API中很多地方也都使用到了Optional。(暂时没有找到特别合适的用法...)

基本方法:

上一篇下一篇

猜你喜欢

热点阅读