Java8新特性(基础知识)
本文是参杂着个人见解的博客笔记,但总体上是依据 <<Java8实战>>这本书的大纲来进行编写,示例也是大多依据其中.
为什么要学习Java8的新特性
Java8版本是Java更新迭代中非常大的一个版本,他改变了许多Java编写的风格,也使许多冗余的程序,变得更为简便和易懂,让我们来看下两端代码的不同,
首先我们有一个需求: 按照重量从小到大排序苹果
Java8之前的代码
Collections.sort(inventory, new Comparator<Apple>() { @Override public int compare(Apple o1, Apple o2) { //compareTo > 返回值为1 调换位置 return o1.getWeight()- o2.getWeight(); } });
在java8之前的代码我们需要多写几行,并且一眼见过去并不能直接明了的理解代码要表达的意思,我们看看下面的代码
java8后的实现代码
// 需要implements Comparable接口 inventory.sort(Comparator.comparing(Apple::getWeight));
可以直接看出这个inventory,想要去排序,并且是通过Apple中的重量去排序.
说个老实话你可能不习惯,因为我开始也是,但是后面你会爱上这种形式的.
首先我们看下有什么不同呢 我们用到了一个Java8中的新接口Comparator,和方法引用Apple::getWeight,这两者我们在后面都会讲解到
Lambda表达式
上面的例子中,我们其实是运用了Lambda表达式,当然,它也不只只有以上的功能,那只是冰山一脚~
那么我们继续看下,这些更新Java的大佬到底想要我们尽量使用lambda到底有什么要注意和方便的
传递代码
这个的思想核心是将代码作为函数传入到方法中,它可以更加灵活的应对不同的要求,不信我们可以看下以下的苹果示例
需求:我要筛选出绿色的苹果
public static List<Apple> filterGreenApples(List<Apple>inventory){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory){ if("green".equals(apple.getColor())){ result.add(apple); } } return result; }
看下以上代码,确实没啥问题,但是,问题是我换需求了,我现在要大于150g的苹果,然后你就复制粘贴得到以下代码
需求:大于150g的苹果
public static List<Apple> filterHeavyApples(List<Apple>inventory){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory){ if(apple.getWeight() > 150){ result.add(apple); } } return result; }
这两段代码其实只有 if(....) 这一行是不同的,也违背了我们不应该要有重复代码的心愿,并且需要复制粘贴~
那接下来我们看下Java8后使用Lambda的写法
public static boolean isGreenApple(Apple apple){ return "green".equals(apple.getColor()); } public static boolean isHeavyApple(Apple apple){ return apple.getWeight() > 150; } //Predicate接口,是一个函数式接口,接受一个函数作为参数,返回一个boolean值,后面会有源码解析 //若要使用可以filterApples(inventory, Apple::isGreenApple); static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory){ if(p.test(apple)){ result.add(apple); } } return result; }
这样直接看上去,是感觉跟以前的写法不同的,不过是将方法抽取出来的一种感觉,但是别着急,我们慢慢来改进下.
但是 我们也应该知道代码数是减少了,也更加明亮了,我们看下面改进下
传递方法到Lambda
filterApples(inventory, (Apple a) -> "green".equals(a.getColor())); filterApples(inventory, (Apple a) -> a.getWeight() > 150);
有人会想,这箭头是啥玩意,但若你学过javascript你应该更容易理解,我们现在可以在参数中写我们想要筛选苹果的需求,但这是不够的.
现在我们从上面的对比中是可以发现lambda的写法确实比以前好的多,可是优秀不了很多,所以新的需求来了
应对多变的需求
筛选绿苹果
public static List<Apple> filterGreenApples(List<Apple>inventory){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory){ if("green".equals(apple.getColor())){ result.add(apple); } } return result; }
由上,绿色被固定了不能变化颜色需求,需要改来改去,我们以前手段可以尝试将其抽象化,用参数color传入方法中
把颜色作为参数
public static List<Apple> filterApplesByColor(List<Apple>inventory, String color){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory){ 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<>(); for(Apple apple : inventory){ if(apple.getWeight() > weight){ result.add(apple); } } return result; }
这样一看,妈耶怎么日常DRY(don't repeat yourself)
现在我们在来狠点的,要多点需求同时进行的
需求:重量大于150,颜色要绿色,可能你会使用以下代码
public static List<Apple> filterApplesByWeightAndColor(List<Apple>inventory, int weight, String color){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory){ if(apple.getWeight() > weight && apple.getColor().equals(color)){ result.add(apple); } } return result; }
if(...)这一行语句又重复了,并且在类中这次又多个方法,变复杂了,虽然两个需求能应付,那么多上十来个需求呢,这并不是不可能的!
所以我们使用这种方法会相对简单点
public static boolean isGreenApple(Apple apple){ return "green".equals(apple.getColor()); } public static boolean isHeavyApple(Apple apple){ return apple.getWeight() > 150; } //Predicate接口,是一个函数式接口,接受一个函数作为参数,返回一个boolean值,后面会有源码解析 //若要使用可以filterApples(inventory, Apple::isGreenApple); static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory){ if(p.test(apple)){ result.add(apple); } } return result; }
但这其实也是在需求多时也是特别复杂的,所以我们要解决这些啰嗦的东东
现在我们可以使用匿名类来去解决这一类的问题
首先我们需要一个接口
public interface ApplePredicate { boolean test (Apple apple); }
在测试类中
List<Apple> redApples = filterApples(inventory, new ApplePredicate(){ @Override public boolean test(Apple apple) { return "red".equals(apple.getColor()); } }); List<Apple> WeightApples = filterApples(inventory, new ApplePredicate(){ @Override public boolean test(Apple apple) { return apple.getWeight() > 150; } }); static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) { List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (p.test(apple)) { result.add(apple); } } return result; }
这样会让人舒服很多,重复代码也变少了,但是匿名类笨重,因为需要很多的空间,并且我个人有时对匿名内部类用起来挺费解的
现在我们用Lambda来尝试
首先也需要一个接口
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
在测试类中
public class test { public static void main(String[] args) { //inventory 库存量 List<Apple> inventory = new ArrayList<Apple>(); Apple apple2 = new Apple(100, "green"); Apple apple1 = new Apple(200, "red"); Apple apple3 = new Apple(150, "green"); Apple apple4 = new Apple(170, "pink"); inventory.add(apple2); inventory.add(apple1); inventory.add(apple3); inventory.add(apple4); List<Apple> redApples = filterApples(inventory,(Apple a) -> "red".equals(a.getColor())); List<Apple> WeightApples = filterApples(inventory,(Apple a) -> a.getWeight() > 150); System.out.println(redApples); System.out.println(WeightApples); } static <T>List<T> filterApples(List<T> inventory, Predicate<T> p) { List<T> result = new ArrayList<>(); for (T t : inventory) { if (p.test(t)) { result.add(t); } } return result; } }
看这比以前的代码简洁了很多不是? 提供了一个泛型的方法,也可以运用到其他方法, 灵活而且简洁!
再来个helloworld例子
/*Thread d = new Thread(new Runnable(){ public void run (){ System.out.println("hello,world"); }});*/ //变成如下形式 Thread d = new Thread(() -> { System.out.println("hello,world"); });
那Lambda的形式到底该如何书写呢
(Apple a) -> "green".equals(a.getColor()) 由上可知lambda表达式由三部分组成 参数 箭头 主体 这里要注意得是主体, 当主题只有一行时, 可以省略return, 若是主题被{}所包裹,主题必须两行以上,或者有return语句,以下是几个例子 (String s) -> s.length();//省略了return语句,检测返回值为int (Apple a) -> a.getWeight() > 150 //省略了return语句,检测返回值为boolean (int x, int y) -> { System.out.println("result:"); System.out.println(x+y); } //两个参数没有返回值
更多得例子可以在网上搜查
Lambda在哪使用呢
lambda使用得条件,必须是有函数式接口得地方,什么叫函数式接口呢,例如前面提起得Predicate<T>
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
通过查看源码每个函数式接口只能有并且有一个唯一得抽象方法,只有如此,才可与我们写得函数联系在一起,那让我们再看一下@FunctionalInterface标签
/** * An informative annotation type used to indicate that an interface * type declaration is intended to be a <i>functional interface</i> as * defined by the Java Language Specification. * * Conceptually, a functional interface has exactly one abstract method. 函数式接口只有一个抽象方法 Since {@linkplain java.lang.reflect.Method#isDefault() * default methods} have an implementation, they are not abstract. If * an interface declares an abstract method overriding one of the * public methods of {@code java.lang.Object}, that also does * <em>not</em> count toward the interface's abstract method count * since any implementation of the interface will have an * implementation from {@code java.lang.Object} or elsewhere. * * <p>Note that instances of functional interfaces can be created with * lambda expressions, method references, or constructor references. 函数式接口可被lambda表达式,方法引用,构造函数引用使用 * * <p>If a type is annotated with this annotation type, compilers are * required to generate an error message unless: * * <ul> * <li> The type is an interface type and not an annotation type, enum, or class. * <li> The annotated type satisfies the requirements of a functional interface. * </ul> * * <p>However, the compiler will treat any interface meeting the * definition of a functional interface as a functional interface * regardless of whether or not a {@code FunctionalInterface} * annotation is present on the interface declaration. * * @jls 4.3.2. The Class Object * @jls 9.8 Functional Interfaces * @jls 9.4.3 Interface Method Body * @since 1.8 //java8开始 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
通过以上源码我们可知道函数式接口得用法