函数式接口的思考
接口和抽象类的区别还在吗?抽象类还有意义吗?
-
接口中可以有抽象方法,默认方法,静态反方法;抽象类中可以有抽象方法,实例方法,静态方法。
-
接口中的属性是 public static final 的;抽象类中的属性则不是。
-
接口中的方法只能是public的,抽象了则不是
-
接口可以多实现,抽象类只能单继承
再看看JDK中对于抽象类和接口的应用,抽象类往往是实现接口中一些通用的方法,而子类只需要集成抽象类,实现个性化的方法即可。总而言之,接口定义了子类的行为,其中部分行为对于子类来说是一致的,这些行为适合放在抽象类中实现,对于子类个性化行为,应放在具体的子类中实现。
函数式接口的实例到底是什么?
函数式接口的实例,例如下面一段代码:
Function<String,String> function1 = (String a) -> a;
function1是函数式接口的一个实例,这个实例的类型是什么?Function只是一个接口,在Java中接口产生对象只能依附于其实现类,但是这儿我们并没有看见其实现类,但是为什么却有了一个实例?
System.out.println(function1.getClass());
System.out.println(function1.getClass().getInterfaces().length);
System.out.println(function1.getClass().getInterfaces()[0]);
System.out.println(function1.getClass().getSuperclass());
// 输出结果:
class com.lhf.ts.jdk.function.FunctionalTest$$Lambda$1/809762318
1
interface java.util.function.Function
class java.lang.Object
从结果中可以总结出:
-
函数式接口对象的类型是JDK自动生成的类型:FunctionalTest$$Lambda$1/809762318
-
函数式接口实例实现了一个接口,就是对应的函数式接口
-
函数式接口的实例的父类是java.lang.Object。
第三点我觉得是比较重要的。因为所有函数式接口的实例类型都是java.lang.Object的子类,所以接口中的抽象方法如果是覆盖java.lang.Object的public方法,则不算做抽象方法。因为子类中必然包含了java.lang.Object中public方法的实现,如果子类自己没有实现,也默认继承了Java.lang.Object的实现,因此子类字节码中一定是有实现的。
静态方法和默认方法有什么问题吗?
JDK8在接口中引入了静态方法和默认方法,这种设计思路使得JDK8中新增的特性完全兼容JDK以前版本的代码。以java.util.List接口为例,如果还是和以前一样,接口中不能有方法的实现,JDK8如果在java.util.List接口中增加了sort方法,那么我们以前线上的代码如果某个类实现了java.util.List接口,那升级JDK8后编译直接报错,接口中的抽象方法子类中没有实现。
但是有没有发现一些问题
example1:
public interface MyInterface {
default void test1(){
System.out.println("MyInterface test1");
}
}
public abstract class MyAbsClass{
public void test1(){
System.out.println("MyAbsClass test");
}
}
public class TestClass extends MyAbsClass implements MyInterface {
public static void main(String[] args) {
TestClass test = new TestClass();
test.test1();
}
}
上例中,test既是抽象类MyAbsClass的实例,又是接口MyInterface的实例,那调用test.test1()方法,到底是调用抽象类的呢?还是调用接口的???
答案是:抽象类的。 在JDK中,默认为类的调用级别比接口的高。
example2:
public interface MyInterface {
default void test1(){
System.out.println("MyInterface test1");
}
}
public interface MyInterface2 {
default void test1(){
System.out.println("MyInterface2 test1");
}
}
public class TestClass implements MyInterface,MyInterface2 {
public static void main(String[] args) {
TestClass test = new TestClass();
test.test1();
}
}
这种情况呢?都是接口级别的,那到底调用的是接口1的实现呢?还是接口2的实现呢?如果你将这段代码敲到IDEA里,就会发现,编译器检查出来不知道该调用哪个接口的,强制要求你在当前类中重写test1()方法,因此调用的是当前类重写的test1()方法。
函数式接口中的异常?
回想我们在JDK8之前的编程中,如果出现了未捕获的异常,异常会一层一层往上抛给方法的调用者。在函数式接口中一样,如果出现未捕获异常,则方法会从异常抛出的地方结束,将异常向上逐层抛给方法的调用者。
总结
函数式接口在运行中,其实JDK产生对应的LambdaClass类型,这个类型同样是java.lang.Object的子类型,而函数式接口中又仅有一个未实现的抽象方法,那么在这个类型中,将函数式接口实例指向的函数作为这个抽象方法的实现。后面Stream API中将会大量的使用到函数式接口,例如使用lambda,方法引用或者构造方法引用去构造函数式接口的对象,其思想都是通过动态生成Class类型,利用字节码技术,将方法动态替换成使用时指定的行为,然后去调用这个行为实现的。