带有默认方法的Java 8接口启用Jacoco时不起作用的方法

Din*_*ian 7 java android java-8 jacoco kotlin

我们有一个带有默认方法的接口,我们在两个JavaKotlin类中实现了该接口,我们提供了非默认方法的实现.

当我们在调试模式(没有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)
Run Code Online (Sandbox Code Playgroud)

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

如果我们将minSdk更改为24,它testCoverageEnabled = true本身就可以正常工作.我们无法弄清楚确切的问题.

Mir*_*ili 2

如果您想要调用某个方法的实现,而该方法在您的类中显式地default没有实现,则可能会出现此问题。(但在该基础(父级、超级)中有一个实现)。defaultinterfaceimplementsdefault interfaceinterface

示例:假设这些定义:

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() { /* ...*/ }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

然后执行:

A a = new A();
a.methodFromBaseInterface(); // This cause above error!
Run Code Online (Sandbox Code Playgroud)

你会在提到的地方得到一个错误!

(旁注:您可能需要定义至少一种方法DerivedInterface以避免NoClassDefFoundError在运行时获取!)


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

我认为这个问题与Android平台中Java 8语言API的不完整支持有关:

Android Studio 3.0 及更高版本支持所有 Java 7 语言功能以及部分 Java 8 语言功能(因平台版本而异)

特别请参阅该页面中的Java 8 Language APICompatible minSdkVersion表:

java.lang.FunctionalInterface:API 级别 24 或更高。


我发现的解决方法:

  1. DerivedInterface如果您有权访问简单覆盖的定义methodFromBaseInterface并将其显式委托给其super接口:

    interface DerivedInterface extends BaseInterface {
        @Override default void methodFromBaseInterface() {
            BaseInterface.super.methodFromBaseInterface();
        }
        // ...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 定义一个class实现它BaseInterfaceA从中派生的中间体。然后运行methodFromBaseInterface间接抛出中间class

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

    注意:/*extends B*/如果您的A类以前有一个super命名的B.