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.
How to avoid Diamond Problem With Default Methods in Java 8. In order to solve this error, you need to override the write() method in your implementation class i.e. class Multitalented here, this will remove the ambiguity, making the compiler happy enough to compile this class.
Default methods enable you to add new functionality to existing interfaces and ensure binary compatibility with code written for older versions of those interfaces. In particular, default methods enable you to add methods that accept lambda expressions as parameters to existing interfaces.
The default methods were introduced to provide backward compatibility so that existing interfaces can use the lambda expressions without implementing the methods in the implementation class. Default methods are also known as defender methods or virtual extension methods.
Like regular interface methods, default methods are implicitly public; there's no need to specify the public modifier. Unlike regular interface methods, we declare them with the default keyword at the beginning of the method signature, and they provide an implementation.
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.
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();
}
// ...
}
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With