启用 Jacoco 时,具有默认方法的 Java 8 接口(interface)不起作用

标签 java android java-8 kotlin jacoco

我们有一个带有 默认方法 的接口(interface),我们在 JavaKotlin 类中都实现了该接口(interface),我们为非默认方法。

当我们在 Debug模式下运行(没有 testCoverageEnabled = true)并且应用按预期运行时。但是当我们在 testCoverageEnabled = true 的不同配置中运行时,应用程序崩溃并出现以下错误

java.lang.NoSuchMethodError: No static method $$triggerInterfaceInit()V in class Lcom/ui/viewholders/CAViewContract$$CC; or its super classes (declaration of 'ui.viewholders.CAViewContract$$CC' appears in /data/app/SMCXbiLYvHb1Kk08Kee__g==/base.apk)
    at home.c.CCFragment.<clinit>(Unknown Source:0)
    at home.HomePageCardProvider.getFragment(HomePageCardProvider.java:17)
    at home.HomeFragment.handleCardFragment(HomeFragment.java:172)

注意: 1. JaCoCo 版本:“0.8.0” 2. 操作系统:Android with minSdk 21

如果我们将 minSdk 更改为 24,并且 testCoverageEnabled = true 本身,它就可以工作。我们无法找出确切的问题。

最佳答案

如果您要调用在 interface 中没有 default 实现的方法的 default 实现,则可能会出现此问题你的类明确地实现它。 (但在该 interface< 的 base (parent, super) interface 中有 default 实现)。

示例:假设这些定义:

class A implements DerivedInterface /*, ...*/ {
    @Override public void methodFromBaseInterface() {
        DerivedInterface.super.methodFromBaseInterface(); // Error:
            // NoSuchMethodError: No static method methodFromBaseInterface
    }
    // ...
}

interface DerivedInterface extends BaseInterface {
    // ... 
    // `methodFromBaseInterface` hasn't overriden here.
}

interface BaseInterface {
    default void methodFromBaseInterface() { /* ...*/ }
    // ...
}

然后执行:

A a = new A();
a.methodFromBaseInterface(); // This cause above error!

你在提到的地方遇到了一个错误!

(附注:您可能需要在 DerivedInterface 中定义至少一个方法以避免在运行时出现 NoClassDefFoundError!)


这类似于一个错误! 我们使用了 super 关键字。为什么期望 static 方法?!! 还有一点,上面的代码没有任何问题,你可以在任何 Java 8 兼容的环境中运行它而无需任何问题!

我认为问题与 Android 平台对 Java 8 语言 API 的支持不完全有关:

Android Studio 3.0 and later supports all Java 7 language features and a subset of Java 8 language features that vary by platform version.

具体见that page中的Java 8 Language API兼容的minSdkVersion表:

java.lang.FunctionalInterface : API level 24 or higher.


我找到的解决方法:

  1. 如果您有权访问 DerivedInterface 的定义,只需重写 methodFromBaseInterface 并将其显式委托(delegate)给它的 super 接口(interface):

    interface DerivedInterface extends BaseInterface {
        @Override default void methodFromBaseInterface() {
            BaseInterface.super.methodFromBaseInterface();
        }
        // ...
    }
    
  2. 定义一个实现BaseInterface的中间class并从中派生出A。然后运行methodFromBaseInterface间接抛出中间class:

    class MiddleClass /*extends B*/ implements BaseInterface {}
    
    class A extends MiddleClass implements DerivedInterface {
        @Override public void methodFromBaseInterface() {
            super.methodFromBaseInterface(); // Indirectly from `MiddleClass`
        }
        // ...
    }
    

    注意:如果您的 A 类之前有一个名为 Bsuper,请取消注释 /*extends B*/ .

关于启用 Jacoco 时,具有默认方法的 Java 8 接口(interface)不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50714637/

相关文章:

android - 在运行时为 Android 检索 apk 签名

java - IntStream rangeClosed 无法返回 int 以外的值

Java 可选 - If Else 语句

java - 使用 Hibernate 查询

Java:在不进行强制转换的情况下使用泛型和映射/@SuppressWarnings

android - 不管怎样从时间上得到上午/下午?

android - 通过 http 安全 Android 服务器数据上传

java - 如何重定向 Groovy 脚本的输出?

java - 同时具有java和scala的maven项目

Java - Java8 中的 JVM 垃圾收集器