为什么Java中的Collection类都继承了抽象类还要实现抽象类的接口?

Lin2J
2021-01-14 / 0 评论 / 398 阅读
温馨提示:
本文最后更新于2021-01-14,若内容或图片失效,请留言反馈。

StackOverFlow的答案在这里 传送门

在Java的集合类中,有很多这种操作,一个集合类实现了抽象类后,又显式地实现抽象类实现的接口。我通过一段代码来解释为什么要这样,也可以直接跳过代码,看好处。

解释一下:
现在有四个接口 A,B,C,Aa(Aa,实际上是继承了A,B)
有一个抽象类,该抽象类实现了 Aa,就相当于 AbstractList 实现了 List
有两个版本的实现了,一个在类声明的时候显式地声明了自己实现了 A B,一个则没有
public interface A {}
public interface B {}
public interface C {}
public interface Aa extends A, B {}
public abstract class AbstractAa implements Aa{ }
public class AaImplV1 extends AbstractAa implements C{ }
public class AaImplV2 extends AbstractAa implements A, B, C { }

接下来通过代码来测试一下这两种声明方式的区别

import java.util.Arrays;

public class InterfaceTest {

    @Test
    public void interfaceTest(){
        AaImplV1 v1 = new AaImplV1();
        AaImplV2 v2 = new AaImplV2();

        // 输出该类实现的接口
        System.out.println("V1: " + Arrays.toString(v1.getClass().getInterfaces()));
        System.out.println("V2: " + Arrays.toString(v2.getClass().getInterfaces()));

        // 这种机制不影响 instanceof
        System.out.println("V1 instanceof A: " + (v1 instanceof A));
        
        // 使用 Apache Common Lang 包的类来获取该类实现的所有接口
        System.out.println("V1: " + ClassUtils.getAllInterfaces(v1.getClass()));
        System.out.println("V2: " + ClassUtils.getAllInterfaces(v2.getClass()));
    }
}
输出
V1: [interface com.jia.interfaces.C]
V2: [interface com.jia.interfaces.A, interface com.jia.interfaces.B, interface com.jia.interfaces.C]
V1 instanceof A: true
V1: [interface com.jia.interfaces.C, interface com.jia.interfaces.Aa, interface com.jia.interfaces.A, interface com.jia.interfaces.B]
V2: [interface com.jia.interfaces.A, interface com.jia.interfaces.B, interface com.jia.interfaces.C, interface com.jia.interfaces.Aa]

再来看看二者的依赖图

AaImplV1AaImplV2
201905192015354692019051920155055

可以看出跟测试程序输出的结果有点像,显式声明的接口才会被打印出来。不过好在这种机制不会影响 instanceof 关键字,可以看测试代码。
虽然这两种实现方式的效果是一样的,但是建议使用V2的方式,好处是

  • 规范:相比较V1,我们在看V2的类声明时就知道该类实现的接口有哪一些,而不用追溯到整个类结构。
  • 可读性:可以更方便地获取该类实现的所有接口。像V1这样隐式实现的,不能直接获取所有接口,而是通过递归迭代类层次结构,或者使用Apache Common Lang 包里的ClassUtils类(实质也是递归查询,看下面代码)

ClassUtils.getAllInterfaces(Class<?> cls) 的原理,递归查询每个类实现的接口,然后放在一个LinkHashSet中。有兴趣可以自己去看ClassUtils的源码。

    private static void getAllInterfaces(Class<?> cls, final HashSet<Class<?>> interfacesFound) {
        while (cls != null) {
            final Class<?>[] interfaces = cls.getInterfaces();

            for (final Class<?> i : interfaces) {
                if (interfacesFound.add(i)) {
                    getAllInterfaces(i, interfacesFound);
                }
            }

            cls = cls.getSuperclass();
         }
     }