四、方法引用
方法引用使用一对冒号::
,通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
下面,我们在Car
类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。
public class Car { //Supplier是jdk1.8的接口,这里和lamda一起使用了 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()); } }
4.1、构造器引用
它的语法是Class::new
,或者更一般的Class< T >::new
,实例如下:
final Car car = Car.create( Car::new ); final List< Car > cars = Arrays.asList( car );
4.2、静态方法引用
它的语法是Class::static_method
,实例如下:
cars.forEach( Car::collide );
4.3、类的成员方法引用
它的语法是Class::method
,实例如下:
cars.forEach( Car::repair );
4.4、实例对象的成员方法的引用
它的语法是instance::method
,实例如下
final Car police = Car.create( Car::new ); cars.forEach( police::follow );
注意:这个方法接受一个Car
类型的参数!
运行上述例子,可以在控制台看到如下输出:
Collided com.example.jdk8.methodrefer.Car@15aeb7ab Repaired com.example.jdk8.methodrefer.Car@15aeb7ab Following the com.example.jdk8.methodrefer.Car@15aeb7ab
五、默认方法
Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。
默认方法使得开发者可以在不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。
为什么要有这个特性?首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
默认方法、静态方法语法格式如下:
public interface Vehicle { //默认方法 default void print(){ System.out.println("我是一辆车!"); } // 静态方法 static void blowHorn(){ System.out.println("按喇叭!!!"); } }
我们可以通过以下代码来了解关于默认方法的使用,实例如下:
public class Tester { public static void main(String args[]){ Vehicle vehicle = new Car(); vehicle.print(); } } interface Vehicle { default void print(){ System.out.println("我是一辆车!"); } static void blowHorn(){ System.out.println("按喇叭!!!"); } } interface FourWheeler { default void print(){ System.out.println("我是一辆四轮车!"); } } class Car implements Vehicle, FourWheeler { public void print(){ Vehicle.super.print(); FourWheeler.super.print(); Vehicle.blowHorn(); System.out.println("我是一辆汽车!"); } }
执行以上脚本,输出结果为:
我是一辆车! 我是一辆四轮车! 按喇叭!!! 我是一辆汽车!
六、Stream
Java 8 API添加了一个新的java.util.stream
工具包,被称为流 Stream,可以让你以一种声明的方式处理数据,这是目前为止最大的一次对 Java 库的完善。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API 可以极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
+--------------------+ +------+ +------+ +---+ +-------+ | stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect| +--------------------+ +------+ +------+ +---+ +-------+
以上的流程转换为 Java 代码,实例如下:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 获取集合中大于2、并且经过排序、平方去重的有序集合 List<Integer> squaresList = numbers .stream() .filter(x -> x > 2) .sorted((x,y) -> x.compareTo(y)) .map( i -> i*i).distinct().collect(Collectors.toList());
在 Java 8 中,集合接口有两个方法来生成流:
- stream():为集合创建串行流
- parallelStream():为集合创建并行流
当然,流的来源可以是集合,数组,I/O channel, 产生器generator 等!
6.1、filter
filter
方法用于通过设置的条件过滤出元素。以下代码片段使用filter
方法过滤出空字符串。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
6.2、limit
limit
方法用于获取指定数量的流。以下代码片段使用limit
方法打印出 10 条数据:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
6.3、sorted
sorted
方法用于对流进行排序。以下代码片段使用sorted
方法对集合中的数字进行排序:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); numbers.stream().sorted().forEach(System.out::println);
6.4、map
map
方法用于映射每个元素到对应的结果,以下代码片段使用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());
6.5、forEach
forEach
方法用于迭代流中的每个数据。以下代码片段使用forEach
输出集合中的数字:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); numbers.stream().forEach(System.out::println);
6.6、Collectors
Collectors
类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors
可用于返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("筛选列表: " + filtered); String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", ")); System.out.println("合并字符串: " + mergedString);
6.7、统计
一些产生统计结果的收集器也非常有用。它们主要用于int
、double
、long
等基本类型上,它们可以用来产生类似如下的统计结果:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("列表中最大的数 : " + stats.getMax()); System.out.println("列表中最小的数 : " + stats.getMin()); System.out.println("所有数之和 : " + stats.getSum()); System.out.println("平均数 : " + stats.getAverage());
6.8、并行(parallel)程序
parallelStream
是流并行处理程序的代替方法。以下实例我们使用 parallelStream
来输出空字符串的数量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
更多实例,可以参考这里官方 API 文档!