2014年java8就已经被提出来了,越来越多的公司项目也都开始使用了Lambdas表达式,所以本着学习的角度,写一篇主体是Lambdas的文章。
先讲一些java8能用到的新特性:
- 接口当中写实现方法,只需要添加一个default的关键字
public interface IInterface {
default void init(){//default要求最低版本要24 所以基本没用
Log.i("dargon","init");
}
}
static int create(int i){//还可以在接口中设置静态方法
return 1;
}
- Lambdas表达式,Lambdas写出来的代码非常简洁,漂亮,他允许开发者将方法的引用当做参数传进去,但是由此会带来阅读性下降,并且需要了解Lambdas才能看懂他的代码。
- 函数式接口,其实目的也是为了更好的支持lambdas表达式。函数式接口是只包含一个抽象方法的接口,可以加注解@FunctionalInterface ,编译器会注意到这个标注,如果你的接口中定义了第二个抽象方法的话,编译器会抛出异常。可以不加。
@FunctionalInterface //可以不加
public interface IConverter<F,T> {
T convert(F f);
}
IConverter<String,Integer> converter = (from) -> Integer.valueOf(from);
converter.convert("123");//返回123
//说的清楚一点就是创建了一个实现这个接口的子类,实现convert方法,之后就可以直接调用方法了,就是写的非常简洁一点,编译器会自动适配到相应的方法的。
可以写的更加简洁一点:
IConverter<String,Integer> converter = Integer::valueOf;//Java 8 允许你通过::关键字获取方法或者构造函数的的引用。
如果还是不清楚 ,可以看这个三个代码块,从原型到精简版:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {//原型
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
Collections.sort(names, (String a, String b) -> { return b.compareTo(a);});//精简版1
//当只有一行代码的时候 你甚至可以不要return 跟大括号
Collections.sort(names,(String a, String b) -> b.compareTo(a));//精简版2
Collections.sort(names, (a, b) -> b.compareTo(a));//最终版
- ::符号,在java8中被定义了新的特性,可以直接引用已有java类或对象的方法或构造器。
第一种方法引用是构造器引用,它的语法是Class::new,或者更一般的Class< T >::new。请注意构造器没有参数。
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
第二种方法引用是静态方法引用,它的语法是Class::static_method。请注意这个方法接受一个Car类型的参数。
cars.forEach( Car::collide );
第三种方法引用是特定类的任意对象的方法引用,它的语法是Class::method。请注意,这个方法没有参数。
cars.forEach( Car::repair );
最后,第四种方法引用是特定对象的方法引用,它的语法是instance::method。请注意,这个方法接受一个Car类型的参数
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
- 重复注解:之前注解在一个地方只能声明一次,java8可以使相同的注解在同一个地方声明多次,通过数组的形式。
- 扩展注解的支持,java8可以为任何东西添加注解,局部变量、泛型类、父类与接口的实现,就连方法的异常也能添加注解。
- 对于编译器的优化:java8通过反射可以获取到参数的名字
- 类库的增加:Optional类,通过它来做空指针的判断
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。更多详情请参考[官方文档](http://docs.oracle.com/javase/8/docs/api/)。
我们下面用两个小例子来演示如何使用Optional类:一个允许为空值,一个不允许为空值。
Optional< String > fullName = Optional.ofNullable(null);
System.out.println("Full Name is set? "+ fullName.isPresent() );
System.out.println("Full Name: "+ fullName.orElseGet( () ->"[none]") );
System.out.println( fullName.map( s ->"Hey "+ s +"!").orElse("Hey Stranger!") );
如果Optional类的实例为非空值的话,isPresent()返回true,否从返回false。为了防止Optional为空值,orElseGet()方法通过回调函数来产生一个默认值。map()函数对当前Optional的值进行转化,然后返回一个新的Optional实例。orElse()方法和orElseGet()方法类似,但是orElse接受一个默认值而不是一个回调函数。下面是这个程序的输出:
Full Name isset?false
Full Name: [none]
Hey Stranger!
让我们来看看另一个例子:
Optional< String > firstName = Optional.of("Tom");
System.out.println("First Name is set? "+ firstName.isPresent() );
System.out.println("First Name: "+ firstName.orElseGet( () ->"[none]") );
System.out.println( firstName.map( s ->"Hey "+ s +"!").orElse("Hey Stranger!") );
System.out.println();
下面是程序的输出:
First Name isset?true
First Name: Tom
Hey Tom!
还有一个stream类,他可以很大程度上简化对集合的处理步骤跟效率,写法上跟rxjava其实很类似。
让我们以一个简单的Task类为例进行介绍:
public class Streams {
private enum Status { OPEN, CLOSED };
private static final class Task {
private final Status status;
private final Integer points;
Task( final Status status,final Integer points ) {
this.status = status;
this.points = points;}
public Integer getPoints() { return points;}
public Status getStatus() {
return status;
}
@Override
public String toString() {
return String.format("[%s, %d]", status, points );
}
}
}
Task类有一个分数的概念(或者说是伪复杂度),其次是还有一个值可以为OPEN或CLOSED的状态.让我们引入一个Task的小集合作为演示例子:
final Collection< Task > tasks = Arrays.asList(new Task( Status.OPEN,5),new Task( Status.OPEN,13),new Task( Status.CLOSED,8));
我们下面要讨论的第一个问题是所有状态为OPEN的任务一共有多少分数?在Java 8以前,一般的解决方式用foreach循环,但是在Java 8里面我们可以使用stream:一串支持连续、并行聚集操作的元素。
// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();
System.out.println("Total points: "+ totalPointsOfOpenTasks );
程序在控制台上的输出如下:
Total points: 18
这里有几个注意事项。第一,task集合被转换化为其相应的stream表示。然后,filter操作过滤掉状态为CLOSED的task。下一步,mapToInt操作通过Task::getPoints这种方式调用每个task实例的getPoints方法把Task的stream转化为Integer的stream。最后,用sum函数把所有的分数加起来,得到最终的结果。
在继续讲解下面的例子之前,关于stream有一些需要注意的地方(详情[在这里](http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps)).stream操作被分成了中间操作与最终操作这两种。
中间操作返回一个新的stream对象。中间操作总是采用惰性求值方式,运行一个像filter这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生了一个新的stream对象,这个新的stream对象包含原始stream中符合给定谓词的所有元素。
像forEach、sum这样的最终操作可能直接遍历stream,产生一个结果或副作用。当最终操作执行结束之后,stream管道被认为已经被消耗了,没有可能再被使用了。在大多数情况下,最终操作都是采用及早求值方式,及早完成底层数据源的遍历。
stream另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算task分数和的例子。
// Calculate total points of all tasks
final double totalPoints = tasks .stream() .parallel() .map( task -> task.getPoints() ) // or map( Task::getPoints )
.reduce(0, Integer::sum );
System.out.println("Total points (all tasks): "+ totalPoints );
这个例子和第一个例子很相似,但这个例子的不同之处在于这个程序是并行运行的,其次使用reduce方法来算最终的结果。下面是这个例子在控制台的输出:
Total points (all tasks): 26.0
经常会有这个一个需求:我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求,下面是一个例子:
// Group tasks by their status
final Map< Status, List< Task > > map = tasks.stream() .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
这个例子的控制台输出如下:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
让我们来计算整个集合中每个task分数(或权重)的平均值来结束task的例子。
// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks.stream() // Stream< String >
.mapToInt( Task::getPoints ) // IntStream
.asLongStream() // LongStream
.mapToDouble( points -> points / totalPoints ) // DoubleStream
.boxed() // Stream< Double >
.mapToLong( weigth -> (long)( weigth *100) )// LongStream
.mapToObj( percentage -> percentage +"%") // Stream< String>
.collect( Collectors.toList() ); // List< String >
System.out.println( result );
下面是这个例子的控制台输出:
[19%, 50%, 30%]
最后,就像前面提到的,Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。下面用一个例子来应证这一点。
final Path path =new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );}
对一个stream对象调用onClose方法会返回一个在原有功能基础上新增了关闭功能的stream对象,当对stream对象调用close()方法时,与关闭相关的处理器就会执行。
还有时间类Clock,并不做过多介绍了,毕竟代码还是熟悉的写起来才快,不是说原生的就是最好的。
如果出现异常或者警告,或者显示不支持lambdas表达式,可以参照这篇博客
http://blog.csdn.net/nicolelili1/article/details/52275263
参考资料:
http://www.importnew.com/11908.html
http://blog.csdn.net/chenleixing/article/details/47802675