7

We are having an interface with default methods and we implemented that interface in both Java and Kotlin classes and we provided the implementation for the non default methods.

When we run in debug mode (which doesn't have testCoverageEnabled = true) and the app works as expected. But when we run in different config with testCoverageEnabled = true, the app is crashing with below error

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)

Note: 1. JaCoCo version: "0.8.0" 2. Operating system: Android with minSdk 21

If we change the minSdk to 24, with testCoverageEnabled = true itself, it is working. We are not able to figure out the exact problem.

2

1 Answer 1

2

This problem can occur if you want to invoke the default implementation of a method that hasn't a default implementation in the interface that your class explicitly implements it. (But has a default implementation in a base (parent, super) interface of that interface).

Example: Suppose these defenitions:

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() { /* ...*/ }
    // ...
}

Then execute:

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

And you get an error at mentioned point!

(Marginal note: You may need to define at least one method in DerivedInterface to avoid getting NoClassDefFoundError at runtime!)


This is similar to a bug! We used super keyword. Why expected static method?!! Another point is that the above code hasn't any problem and you can run it in any Java 8 compatible environment without any problem!

I think the problem is related to incomplete support of Java 8 language APIs in Android platform:

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.

Specially see Java 8 Language API and Compatible minSdkVersion table in that page:

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


Workarounds I found:

  1. If you have access to the definition of DerivedInterface simply override methodFromBaseInterface and explicitly delegates it to its super interface:

    interface DerivedInterface extends BaseInterface {
        @Override default void methodFromBaseInterface() {
            BaseInterface.super.methodFromBaseInterface();
        }
        // ...
    }
    
  2. Define a middle class that implements BaseInterface and derive A from it. Then run methodFromBaseInterface indirectly throw the middle class:

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

    Note: Uncomment /*extends B*/ if your A class previously has a super named B.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.