Appearance
有点干货 | 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的接口