Java8新特性总结

一.接口内允许添加默认实现的方法

在原来的定义中接口中只能有方法声明,不能有方法体。在Java8中,接口也可以有自己带有实现的方法啦。具体来说是要用default来修饰的方法,其可以像类中的方法一样有执行语句。在实现接口时,可以不实现其default方法,并且实现类对象可以调用其接口的default方法。当然也可以在实现类中覆盖default方法。

public class InterfaceDemo {
    public static void main(String[] args) {
        Formula formula = new Formula() {
            @Override
            public double calculate(int a) {
                return a + 1;
            }

//           @Override
//            public double sqrt(int a) {
//              return a;
//            }
        };

        System.out.println(formula.calculate(100));
        System.out.println(formula.sqrt(100));
    }
}

interface Formula {
    //计算
    double calculate(int a);

    //求平方根
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

二.Lambda表达式

Lambda简化了匿名内部类的写法。Java8中可以通过类型推断来判断出用户的意图,不必将类型等信息写全。特别是方法实现体中只有一句语句的实现类,更能加大简化力度。

Lambda解决了将一个方法作为参数传值的问题。解决了一个函数是否可以独立存在的问题。是Java向函数式编程的一种靠拢。

一般在某个方法只使用一次的地方使用Lambda表达式;如果方法没有入参,则只写一个()->{语句};当只有一个参数,且类型可推断时,()可省略;如果方法体中只有一条语句花括号可以省略;


public class LambdaDemo {
    public static void func1() {
        System.out.println("not use Lambda");
        List strs = Arrays.asList("hello","word","apple","people","sea","book","school","computer");

        //old way 
        Collections.sort(strs,new Comparator() {
            @Override
            public int compare(String a,String b) {
                return b.compareTo(a);
            }
        });

        System.out.println(strs);
    }

    public static void func2() {
        System.out.println("use Lambda");
        List strs = Arrays.asList("hello","word","apple","people","sea","book","school","computer");

        //way 1
        Collections.sort(strs,(String a,String b) -> {
            return a.compareTo(b);
        });

        //way 2 : only one statement 
        Collections.sort(strs,(String a,String b) -> a.compareTo(b));

        //way 3 : omit class
        Collections.sort(strs,(a,b) -> a.compareTo(b));

        System.out.println(strs);
    }
}

三.函数式接口Functional Interface

学习了上述Lambda的内容肯定会有一些疑问:如果接口有多个需要实现的方法 呢,还能使用Lambda?如果可以的话Lambda是如何做推断的。

答案是,使用Lambda时要求接口中只能有一个抽象方法(通过default修饰的带有方法体的接口中的方法不是抽象方法)。

如果一个接口被注解@FunctionalInterface修饰,则该接口只能有一个抽象方法,否则会报错。

四.引用类的构造器及方法

在Lambda中若是直接调用了一个方法,且调用方法的形参和要实现的接口抽象方法形参一致,则可以进一步简写。举例如下:
接口定义:

@FunctionalInterface
interface Converter {
    T convert(F form);
}

Lambda表达式

//旧的写法
Converter convert1 = (from) -> Integer.valueOf(from);
//新的写法
Converter convert2 = Integer::valueOf;

System.out.println(convert1.convert("1234"));

引用其他类型的方法

  • 例子中的方法是Integer类的静态方法,如果是某个类的实例方法,则应该使用一个对象加::来引用。如,obj::func;
  • 如果要调用的一个构造方法(抽象方法返回的是一个对象),则应该这样使用:Integer::new。用new代替构造方法名字。

五.Lambda访问外部变量及接口默认方法

访问局部变量

  • 可以访问局部的final变量,但不能修改。
  • 与匿名内部类不同的是,外部变量不需要显示地声明为final,但却要有final的特点,不能被修改,在Lambda之后被修改也不行。

访问成员变量和静态变量
可以任意读写,举例如下:

public class FunctionInterface {
    String str = "init";
    static Integer num = 0;

    public void testScope() {
        Converter convert = (from) -> {
            //在这里可访问成员变量和静态变量
            str = from;
            num = Integer.valueOf(from);
            return num;
        };

        System.out.println(convert.convert("3453"));
        System.out.println(convert.convert("5678"));
    }        
}

访问接口的默认方法
在匿名类中可以访问接口定义的默认方法,在Lambda中不可以访问。

六.内置函数式接口

Java8中内置了许多函数式接口,包括Comparator和Runnable等,它们被添加了@FunctionalInterface注解,以用来支持Lambda表达式。

6.1Predicate断言

查看源码,这个函数式接口中要实现的方法为:boolean test(T t); 即一个判断传入值真假的方法,当然判断的规则由你自己定义。
如定义一个判断字符串长度是否大于10的Predicate:

import java.util.function.Predicate;

public class PredicateDemo {
    public static void main(String[] args) {
        Predicate predicate = s -> s.length() > 10;

        System.out.println(predicate.test("hello"));
        System.out.println(predicate.test("hello,world!"));
    }
}

6.2 Function

查其源码,需要实现一个R apply(T t)的方法。这个接口提供链式调用、组合的功能。

6.3 Supplier

Supplier personSupplier = Person::new;
personSupplier.get();

6.4 Consumer

??
Consumer greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));

6.5 Optional

参考资料

七.Stream流

什么是Stream流?
参考资料
Stream流提供了一种对集合Collection的方便的操作。分为“中间操作”和“终端操作”两种。中间操作的结果还是返回一个Stream,可以继续操作;而终端操作会返回一个结构不能继续流操作了。

要使用Sream首先要通过Collection的Stream方法获取一个Stream对象。

7.1 Filter过滤 中间操作

筛选出集合中满足一定条件的元素。Stream有一个filter方法,入参是一个Predicate,筛选结果是Predicate.test为true的集合的Stream。下面来看一个筛选出String集合中以"s"开头的String的程序:

List list = Arrays.asList("hello","world","apple","people","sea",
                                  "watch","table","book","school","help");
list
    .stream() //获取stream
    .filter(s -> s.startsWith("s"))  //设置filter。中间操作
    .forEach(System.out::println);   //打印出流中的元素。终端操作

7.2 Sorted排序 中间操作

可以给sorted()方法传入一个Comparator用来自定义排序,否则将使用默认排序规则。

list
    .stream()
    .sorted((a,b) -> b.compareTo(a))
    .forEach(System.out::println);

7.3 Map

map方法入参为一个Function函数式接口。调用map方法将对集合中的每一个元素执行一下Function中的apply方法,并返回由其返回值组成的集合的流。

举例:将表示数字的字符串集合全部转换为数字再加一后输出。

List list = Arrays.asList("10","100","1000");

System.out.println("---- test map ----");
list
    .stream()
    .map(Integer::valueOf)  //转换为整数
    .map(a -> a + 1)        //执行加一操作
    .forEach(System.out::println);

7.4Match匹配

是种终端操作,结果不是stream对象,而是boolean值
根据Predicate指定的规则判断集合中是否有匹配的,有的话返回true。
有三种形式,anyMatch:有一个匹配就返回true。allMatch:全部匹配返回true。noneMatch:全部不匹配返回true。

boolean anyMatch(Predicate predicate);
boolean allMatch(Predicate predicate);
boolean noneMatch(Predicate predicate);

7.5 Count计数

终端操作,统计stream中元素的个数。

long count = 
    list
        .stream() //获取stream
        .filter(s -> s.startsWith("s"))  //设置filter。中间操作
        .count();

7.6 Reduce

list[0]和list[1]执行操作,得到的结果为result。result再和list[2]执行操作,得到的结果result。依次进行,对所有元素执行一遍。

根据上述描述也可以看出,这里的“操作”必须满足两个入参、返回值是同一类型的。

reduce方法的入参是:BinaryOperator 这里的T就是集合的元素类型。

举例:求Integer集合中所有元素的和。

List list = Arrays.asList(1,2,3,4,5);

System.out.println("---- test reduce ----");
Optional sum = 
    list
        .stream()
        .reduce((a,b) -> a + b);

sum.ifPresent(System.out::println);

注意:reduce的返回值为Optional

你可能感兴趣的:(Java8新特性总结)