Java 8 新特性综合概述

阅读更多

 根据Oracle 对java 8开发进度安排,java将于2014年的3月份发布。从java8 新引入的特性看,函数式编程语言对java8 影响很大,运行在JVM上的Scala、Groovy等编程语言部分特性被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接口实现类可以这样写

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 upperfier = String::toUpperCase;
 System.out.println(upperfier.apply("Hello"));

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

 

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

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

 

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

 

  四 接口的改进

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

 

 1 默认方法

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

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

 

 default void forEach(Consumer 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 > Comparator comparing(
            Function keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator & 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来说明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   predicate,map函数参数为Function mapper ,reduce参数为(T identity,BinaryOperator accumulator),identity为起始值,accumulator为二元操作函数。
   x -> x > 5为Predicate的lambda写法,(x , y ) -> x + y 为BinaryOperator accumulator的lambda写法

 

3 Stream的实际应用

当年学习Oracle时入门的例子就用employee表,这里我举例还是用Employee,只是从数据库表换成里java类。现有一个List 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 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> 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可以轻轻松松实现很多运算方法。

 

 

 

 

 

你可能感兴趣的:(java,8,lambda,函数式编程)