有一个共识,使用接口比使用类更好。我当然同意:库法接受ArrayList替代的List将是一个废话。
也有一个共识,就是性能总是一样的。在这里,我的基准要求不同。 接口和抽象类结果都有1到4种实现。当使用两个以上的实现时,性能开始出现差异。我正在寻找这种行为的解释(以及错误共识的由来)。
有一个共识,使用接口比使用类更好。
这太简单了。接口和抽象类都 具有彼此之间的优势。
链接到的答案建议将变量声明为java.util.List,而 不是尽可能声明为java.util.ArrayList 。没错,使用List可以为 您提供更大的灵活性,以便以后选择其他实现类, 因此,当您不需要特定于ArrayList的方法 (例如.trimToCapacity())时,这是一件好事。但是,此建议通常与 接口或类无关,并且如果 java.util.List是抽象类也是如此。
也有一个共识,就是性能总是一样的。
流行的建议是,不要担心 类和接口之间的性能差异,而应该根据良好的 编程原理在它们之间进行选择。这是防止程序员 担心无关紧要的性能差异的好建议。但是有时会 误解为暗示没有区别,这是不正确的。这里 是一个小的差异:类是更快的。
通过类的方法调用,在类中的固定偏移量处有一个 vtable ,并且 在该表内的已知偏移量处找到了所需方法实现的指针,因此跳转到目标非常简单。 但是,尽管一个类只能扩展一个超类,但是一个类可以实现 任意数量的接口,因此通过接口进行方法调用更加 复杂。对于接口调用,它必须先查找类的 接口列表以找到所需的接口,然后才能 在该接口的表中查找方法实现。
当使用两个以上的实现时,性能开始出现 差异。
无论使用类还是使用接口,多态调用都会 在CPU上引起流水线刷新,因为CPU无法提前看到跳转的目标,因此 成本很高。如果在运行时知道调用站点是同 构的(oligo表示“很少”),则性能会急剧提高,因为 好的JVM会专门处理这些情况。对于单态情况,JVM可以 直接跳转到单个目标方法,甚至可以内联。对于 二态情况下,实现了o.f();仿佛(无效语法): if (o.getClass() == A.class) A::f(o) else B::f(o);。
实际上,我不确定为什么在 基准测试中双态情况和单态情况一样快– CPU的分支预测器 难道不会在随机数据上有一半的时间出错吗?也许那里还有其他细微之处