java8
近期,在一个不完全的统计中,java8的普及率已经到达了近80%。
图【1】
相比之前的java版本,下面两个是java8出现带来最大的影响:其一是极大地简化了代码的复杂度尤其是在处理集合以及接口这两个方面。除此之外,java8引入了流概念,来避免或减少使用多线程的同时利用机器多核来加快代码的执行效率。
本篇文章主要介绍java8的第一个特性————lambda表达式。
介绍:
在java7及更早的版本,当我们会遇到这样的场景:当要实现一个接口尤其是匿名类接口,往往需要写很多模板式的代码;当我们要遍历一个集合做出筛选的时候会使用很多for循环以及if判断语句,这些代码往往导致代码显得十分臃肿。
而java8出现的Lambda表达式可以很好地解决上面的问题。它可以让你很简洁地表示一个行为或传递代码。你可以把Lambda表达式看作匿名的功能,它基上就是没有声明名称的方法,但和匿名类一样,它也可以作为参数传递给一个方法。
例子:
//普通表达式1
Runnable runnable2 = new Runnable() {
@Override
public void run() {
System.out.println("a");
}
};
Thread thread1 = new Thread(runnable2);
thread1.start();
//lambda表达式1
Runnable runnable = () -> System.out.println("a");
Thread thread=new Thread(runnable);
thread.start();
//普通表达式2
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("a");
}
});
//lambda表达式2
Thread thread3 = new Thread(()-> System.out.println("a"));
thread.start();
上面的例子通过runnable例子分别使用了普通的匿名类和lambda的方式实现线程接口。可以发现使用Lambda表达式可以使我们的代码变得更加简洁。
在哪里使用:
当然,并不是所有的接口都能使用lambda表达式。只有是函数型接口的时候才能使用。
函数型接口指接口只定义了一个抽象方法的接口。例如ranable方法,只定义了一个run方法:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
其中,为了更好的支持函数式接口,在java8中增加了@FunctionalInterface注解,只要使用了@FunctionalInterface注解的接口只能定义一个抽象方法,否则就会报错。
如何使用:
案例
我们可以通过一个例子一步步掌握和熟悉lambda表达式。以下例子来自java 8实战
首先有一个苹果类:
public class Apple {
private String color;
private int weight;
public Apple() {
}
public Apple(String color, int weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
第一次尝试
现在我们要从苹果堆中找出绿色的苹果。
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<Apple>();
for (Apple apple : inventory) {
if ("green".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
我们传入一堆苹果,逐个判断他的颜色是否是绿色,但是这种方式不利于代码的扩展,当我们需要筛选红色、白色、黄色苹果都需要修改代码。
第二次尝试:颜色参数
我们可以给方法传一个颜色的参数,来满足筛选不同颜色的苹果:
public static List<Apple> filterApplesByColor(List<Apple> lists, String color) {
List<Apple> result = new ArrayList<>();
for (Apple apple : lists) {
if (apple.getColor().equals(color)) {
result.add(apple);
}
}
return result;
}
但是,有一天我们需要筛选不同重量的苹果时,上述的方法不能满足我们需求了,于是我们又新建了另一个方法:
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
List<Apple> result = new ArrayList<Apple>();
for (Apple apple : inventory) {
if (apple.getWeight() > weight) {
result.add(apple);
}
}
return result;
}
虽然满足我们的需求,但是筛选苹果和筛选重量的方法大部分的代码是一样的,只有判别的方式不同,我们可以提取大部分的代码。
第三次尝试:对你能想到的每个属性做筛选
第三种尝试,是增加了字段来判断是来判断苹果还是重量。
public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
List<Apple> result = new ArrayList<Apple>();
for (Apple apple : inventory) {
if ((flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight)) {
result.add(apple);
}
}
return result;
}
但是仔细思考之后,这只方式反而是最不好的。首先,他还是只能筛选一个条件,其次,他还是不能做扩展,例如筛选大小,形状等等。
第四次尝试:行为参数化
我们可以创建一个接口,用它来传递我们的筛选规则
public interface ApplePredicate{
boolean test (Apple apple);
}
public static <T> List<T> filterApple(List<T> lists, ApplePredicate<T> predicate) {
List<T> result = new ArrayList<>();
for (T e : lists) {
if (predicate.test(e)) {
result.add(e);
}
}
return result;
}
public class AppleRedAndHeavyPredicate implements ApplePredicate{ public boolean test(Apple apple){
return "red".equals(apple.getColor())
&& apple.getWeight() > 150;
} }
List<Apple> redAndHeavyApples =filterApples(inventory, new AppleRedAndHeavyPredicate());
这样可以通过用接口的方式来满足我们不同对的苹果进行筛选。
第五次尝试:匿名内部类
但是在第四次尝试中,我们每次新的筛选而创建的类只是用一次,可以使用匿名类类的方式来实现接口。
List<Apple> redApples = filterApples(list, new ApplePredicate() {
@Override
public Boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
第六次尝试:lambda表达式
上面的代码在java 8里可以用Lambda表达式重写为下面的样子:
List<Apple> result =
filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
java原生接口
在java8中增加了原生的函数式接口提供我们来使用lambda,例如所有java.util.function 包下的接口都是函数式接口,都可以使用lambda表达式。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
@FunctionalInterface
public interface Function<T, R>{
R apply(T t);
}
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
速度测试
实质分析
存在的问题