Skip to content

有点干货 | Jdk1.8新特性实战篇(多个案例)

前言 一直想把jdk1.8的新特性整理下,恰好看到老外的git(文后有链接),在这个结构上继续完善了说明和功能,做了41个单元测试案例,方便新人学习。以下内容很干,对于一个萌新小白来说,学习jdk1.8的新特性,基本看一遍就知道个7788了,在熟读两遍最后跟着写一遍,那么在实际项目中就可以运用了。不过!新特性,虽然很好。但如果想用,那么自己一定要看看相对应的源码并多练习,否则真的容易给自己搞晕,又很难阅读。

#零、回顾一个抽象类 在jdk1.8之前,因为接口里只能做方法定义不能有方法的实现,因此我们通常会在抽象类里面实现默认的方法{一般这个默认的方法是抽象后公用的方法,不需要每一个继承者都去实现,只需调用即可}。就像下面这样;

在定义的时候;

java

public abstract class AFormula {

    abstract double calculate(int a);

    // 平方
    double sqrt(int a) {
        return Math.sqrt(a);
    }

}

在使用的时候;

java
@Test
public void test_00() {
    AFormula aFormula = new AFormula() {
        @Override
        double calculate(int a) {
            return a * a;
        }
    };
    System.out.println(aFormula.calculate(2)); //求平方:4
    System.out.println(aFormula.sqrt(2));     //求开方:1.4142135623730951
}

一、在接口中提供默认的方法实现(有点像抽象类)

在jdk1.8里面,不仅可以定义接口,还可以在接口中提供默认的实现。这一个小小的改变却让整个抽象设计都随着改变了!

在定义的时候;

java
public interface IFormula {

    double calculate(int a);

    // 平方
    default double sqrt(int a) {
        return Math.sqrt(a);
    }

}

在使用的时候(一);

java
@Test
public void test_01() {
    IFormula formula = new IFormula() {
        @Override
        public double calculate(int a) {
            return a * a;
        }
    };
    System.out.println(formula.calculate(2));
    System.out.println(formula.sqrt(2));
}

在使用的时候(二);如果只是一里面方式这么使用,那么就没多大意思了。我一直说过;好的代码都很骚!

a; a是一个入参名称,可以其他任何名字 ->a*a; 箭头指向是具体的实现 但是,这样其实不太适合加日志了

java
@Test
public void test_02() {
    // 入参a 和 实现
    IFormula formula = a -> a * a;
    System.out.println(formula.calculate(2));
    System.out.println(formula.sqrt(2));
}

二、Lambda 表达式

因为有接口中可以增加默认的方法实现,那么Java肯定是因为要简化开发才出现的这么个设计。所以你会从各个我们以前的List、Set等等所有接口中看到默认的方法实现。

从一段熟悉的排序列子入手

java
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 方法,入参是一个 List 集合,和一个 Comparator 比较器,以便对给定的 List 集合进行排序。上面的示例代码创建了一个匿名内部类作为入参,这种类似的操作在我们日常的工作中随处可见。

Java 8 中不再推荐这种写法,而是推荐使用 Lambda 表达:

java
Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

上面的这段同样功能的代码块,简短干净了许多。就像婆媳一样可能刚开始看不习惯,但是接触接触就喜欢了。因为,它还可以更加简短优秀;

java
Collections.sort(names, (String a, String b) -> b.compareTo(a));

为了追求极致,我们还可以让它再短点:{当然过你的实现不是一行代码,那么不能这么干}

java
names.sort((a, b) -> b.compareTo(a));

java.util.List 集合现在已经添加了 sort 方法。而且 Java 编译器能够根据类型推断机制判断出参数类型,这样,你连入参的类型都可以省略啦,怎么样,是不是感觉很骚气呢!

java
java.util.List.sort

default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

好了!你以为这就结束了吗,不!它还可以更短!(得益于Comparator接口中还提供了stack默认方法,也就是说接口中不是只可有default默认实现,还可以有静态方法)

names.sort(Comparator.reverseOrder());

三、函数式接口 Functional Interfaces

通过上面的例子我们可以看到通过Lambda可以开发出同样功能的逻辑但是代码却很简单,那么Jvm是如何进行类型推断,并且找到对应的方法呢? 通过官文介绍以及我们使用发现,并不是每个接口都可以缩写成Lambda表达式的开发方式。其实是只有那些函数式接口(Functional Interface)才能缩写成 Lambda 表示式。 所谓函数式接口(Functional Interface)就是只包含一个抽象方法的声明。针对该接口类型的所有 Lambda 表达式都会与这个抽象方法匹配。{另外,只是在接口上添加default并不算抽象方法} 总结:为了保证一个接口明确的被定义为一个函数式接口(Functional Interface),我们需要为该接口添加注解:@FunctionalInterface。这样,一旦你添加了第二个抽象方法,编译器会立刻抛出错误提示。{不填写,但是只写一个default也可以} 定义含有注解@FunctionalInterface的接口