欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 8 新特性综合概述

程序员文章站 2022-05-28 21:49:56
...

 根据Oracle 对java 8开发进度安排,java将于2014年的3月份发布。从java8 新引入的特性看,函数式编程语言对java8 影响很大,运行在JVM上的ScalaGroovy等编程语言部分特性被java 8吸收。平台、API等方面先不关注,仅就从编程语言特性来说java8绝对是一次革命性的的改进,有了java 8 java语言的表达能力、简洁性有了很大提高,跟Scala groovy等JVM上的新兴语言差距缩小了很多。语言特性改进概括起来有以下几点

  •  函数式接口  
  •  Lambda表达式
  • 方法引用
  • 接口的改进
  • Stream
这篇文章我将说说以上几个语言特性,以及对代码编写的影响

一  函数式接口

 所谓函数式接口(FunctionalInterface)是指只有一个抽象方法的接口,比如java 8 之前的Runnabe接口(只有run方法),Comparator接口(comare方法),Comparable(compareTo方法)都属于函数式接口,到了Java 8时代这些接口都被注解为@FunctionalInterface,另外@FunctionalInterface也是java 8 新进入的注解,Java 8新增了一个包java.util.function,在function包下提供了大量常见函数式接口,举例如一下几个

Predicate:谓词,boolean test(T t) 输入类型T,输出布尔值

Supplier 生产者,T  get(),无输入,输出类型T

Consumerer 消息者,void accept(T t),输入T,无输出

Function 函数,R apply(T t ) ,输入T 输出R

 

 二 Lambda

Lambda表达式就是匿名方法,方法可以通过名称被重用,而匿名方法不能在被其它代码所调用。lambda也具有输入参数,方法体,返回值,比如(Object o) ->{System.out.println(o.toString()); return o.toSting();}输入参数为Object o 返回值为String类型,方法体为{}之间的语言

比如方法sum方法

public int sum(int x ,int y){
        return x + y;
}

 改用lambda表达式为

(int x, int y) -> {return x + y;}

 在特点场景下通过java 8 类型推导功能,可以省略lambda输入参数的类型,比如上面的sum方法lambda表达式可以改写为

(x, y) -> {return x + y;}

 lambda表达式方法体内可以访问lamba外的变量,相比匿名内部类访问外部类变量需要final修饰方便多了

 Runnable的run方法没有输入参数,用lambda表达式写Runnable接口实现类可以这样写

   

Runable r = () -> {System.out.println("Hello");}

 

Comparator<String>接口实现类可以这样写

Comparator comparator = (String str1,String str2) ->{return s1.compareToIgnoreCase(s2);}
//由于lambda基于类型推导功能,可以省去str1和str2前面的类型String
Comparator comparator = (str1,str2) ->{return s1.compareToIgnoreCase(s2);}

 

 

三 方法引用

  通过方法引用轻轻松松的实现function as first-classs object(函数作为一类对象)功能,可以将方法作为变量看待,作为参数传递。方法引用符号是::

比如通过 方法引用实现字符串从小写到大写的转换

Function<String, String> upperfier = String::toUpperCase;
 System.out.println(upperfier.apply("Hello"));

 比如Set类里有boolean contains(T t)方法,显然方法属于谓词(Predicate)函数,我用可以这用的使用Set的contains方法

 

 Set<String> knowNames = new HashSet<>();
 knowNames.add("Zhang");
 knowNames.contains("Zhang");

 Predicate<String> isKnowName = knowNames::contains;
 isKnowName.test("Zhang");
 isKnowName.test("Li");

 

以上举的方法引用都是实例方法引用,方法引用包括(此处方法包括静态方法、实例方法、构造方法、数组构造方法) 

 

  四 接口的改进

  java 8 中对接口的功能做了改进,包括默认方法和静态方法。

 

 1 默认方法

在java8以前接口不能定义具体方法,只能定义抽象方法。作为一个通用的库或框架,接口一旦发布之后不能轻易的增加新的抽象方法,否则该接口的具体实现类都必须跟着实现新增的方法。java 8 中为了不破坏接口的实现,允许接口有具体的实现方法,实现方法用关键字default修饰 也叫默认方法。

比如JDK中Iterable接口中新增的默认方法 forEach实现遍历功能

 

 default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

 

 

接口增加了默认方法之后就会存在多继承问题。

  比如接口1和接口2都有相同的默认方法method1,接口3同时继承了接口1和接口2,在接口3以及接口3的实现类里 method1到底是继承接口1还是继承接口2的method1方法呢? java 8 通过引入新的语法来i解决问题,接口3必须重写method1方法,在method1方法里可以指定到底是继承接口1还是接口2,比如下面的代码DrawInteface3的draw必须显示指定继承哪个父接口的方法

 

public interface DrawInterface1 {

    default void draw(){
        System.out.println("DrawInterface1.draw");
    }
}

public interface DrawInterface2 {

    default void draw(){
        System.out.println("DrawInterface2.draw");
    }
}

public interface DrawInterface3 extends DrawInterface1,DrawInterface2 {
    default void draw(){
        //接口名.super.父接口方法名 来指定调用哪个接口方法
        DrawInterface1.super.draw();
    }
}
 

  2 静态方法

  在java 8 以前接口标准库里定义了Collection接口,然后增加一个Collections工具了辅助Collection接口,java 8 里允许定义接口的静态方法(static修饰)

   比如Comparator接口增加了大量的静态放,有了comparing这个静态方法后写比较器非常的轻松

 

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }

//根据名字做比较的比较器
Comparator nameComparator = Comparator.comparing(Employee::getName);

//根据薪水做比较比较器
Comparator salaryComparator = Comparator.comparing(Employee::getSalary);
 

 

 五  Stream 流

1 本质

java8 新增的Stream非之前IO相关的流,Stream的本质是对数据源进行抽象提供一套通用API,引入Stream、函数式接口、lambda等几大特性候java 8 有了一些函数式编程语言味道

Stream实现map filter reduce等操作,Stream的数据源包括数组、集合、IO通道、随机数生成器等,Stream相关接口在java.util.stream包下

 

2 使用例子

现在举个List<Integer>来说明Stream的filter map reduce等常见高阶函数的使用   

//对list求和 
int result = list.stream().reduce(0,(x,y) -> x + y);

//对list过滤(大于5的数字)再求和 
int result = list.stream().filter(x -> x > 5).reduce(0,(x,y) -> x + y);

//对list 过滤(大于5的数字) 映射(求平方) 再求和,先通过mapToInt生成IntStream,调用sum方法 
result = list.stream().filter(x -> x > 5).map(x -> x * x).mapToInt(x -> x).sum();
 filter参数为谓词Predicate<T>   predicate,map函数参数为Function<T,R> mapper ,reduce参数为(T identity,BinaryOperator<T> accumulator),identity为起始值,accumulator为二元操作函数。
   x -> x > 5为Predicate的lambda写法,(x , y ) -> x + y 为BinaryOperator<T> accumulator的lambda写法

 

3 Stream的实际应用

当年学习Oracle时入门的例子就用employee表,这里我举例还是用Employee,只是从数据库表换成里java类。现有一个List<Employee> employees (Employee包括name、salary、departId三个属性),通过Stream的相应方法实现一些常见的"统计功能"

 

//求所有员工薪资总和
        int totalSalary = employees.stream()
                .map(e -> e.getSalary())
                .reduce(0,(x,y) -> x + y);
        System.out.println("totalSalary = " + totalSalary);
 
 //所有雇员姓名
 List<String> nameList = employees.stream()
                .map(e -> e.getName())
                .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
 System.out.println("nameList = " + nameList);
 

 

 

//求平均工资
double averageSalary = employees.stream()
                .mapToInt(e -> e.getSalary())
                .average()
                .getAsDouble();

 

 //求平均工资,用Collectors工具类的帮助方法
averageSalary = employees.stream()
                .collect(Collectors.averagingInt(e -> e.getSalary()));
  System.out.println("averageSalary = " + averageSalary);

 

 //雇员按照部门分组
 Map<Integer,List<Employee>> deaprtEmployee = employees.stream()
                .collect(Collectors.groupingBy(e -> e.getDepartId()));

 

 //打印每个部门的平均工资
        deaprtEmployee.forEach((k,v) -> System.out.println(k+":" + v.stream().mapToInt(
                e -> e.getSalary()).average().getAsDouble()));

 

//查找每个部门工资最低的雇员
deaprtEmployee.forEach((k,v) -> System.out.println(k+":" + v.stream().
                sorted(Comparator.comparing(Employee::getSalary)).findFirst().get()));

 

 //对雇员根据名字排序
employees.sort(Comparator.comparing(Employee::getName));

 

通过以上的例子可以看出java 8 的表达能力有了很大提高,更少的代码能完成更多的事情。另外通过Stream的collect()方法和工具类Collectors可以轻轻松松实现很多运算方法。