Java 8 Interface with default Methods not working when Jacoco Enabled - java

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.

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:
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.

Related

Static Inner Classes Bytecode Java [duplicate]

This question already has answers here:
Why is an anonymous inner class containing nothing generated from this code?
(5 answers)
Closed 7 years ago.
In the below code :
class EnclosingClass
{
public static class BiNode extends Sub.IBiLink { }
private static class Sub
{
private static class IBiLink
{
}
}
}
On compiling along with other .class files, I also see a file named "EnclosingClass$1.class" .Why has this been automatically created? Whats going on?
First have a look at the class access and propery modifier table from the JVM specifications.
Notice the ACC_SYNTHETIC flag which interpretation specify that it is not present in the source code (in simplier words, it will be added when the class is generated by the compiler).
Let's have a look at the bytecode of EnclosingClass$1.class (note that I will paste only the part that matter).
javap -v EnclosingClass$1.class
produce the following result
Classfile /C:/Users/jfrancoiss/Desktop/Nouveau dossier/EnclosingClass$1.class
Last modified 2015-03-31; size 190 bytes
MD5 checksum 5875440f1e7f5ea9a519d02fbec6dc8f
Compiled from "EnclosingClass.java"
class EnclosingClass$1
minor version: 0
major version: 52
flags: ACC_SUPER, ACC_SYNTHETIC
Notice that the access flags of the class contains ACC_SYNTHETIC.
The ACC_SYNTHETIC flag indicates that this class or interface was
generated by a compiler and does not appear in source code.
An other option to make sure the generated class is synthetic is to compile as
javac -XD-printflat EnclosingClass.java
which would produce
/*synthetic*/ class EnclosingClass$1 {
}
Great, but why generate a synthetic class ?
The Java reflection tutorial can help us understand this. Have a look at the comments in the SyntheticConstructor class
public class SyntheticConstructor {
private SyntheticConstructor() {}
class Inner {
// Compiler will generate a synthetic constructor since
// SyntheticConstructor() is private.
Inner() { new SyntheticConstructor(); }
}
}
So according on the comment, the synthetic class EnclosingClass$1.class was created because IBiLink was private.
Once again, the java reflection tutorial specify at this point
Since the inner class's constructor references the private constructor
of the enclosing class, the compiler must generate a package-private
constructor.
In our case, we do not see explicitely any constructor call, but we have this line
public static class BiNode extends Sub.IBiLink { }
Let's try compiling this code and see what happen
class EnclosingClass
{
//public static class BiNode extends Sub.IBiLink { }
private static class Sub
{
private static class IBiLink
{
}
}
}
No EnclosingClass$1.class generated.
More details noticed when debugging
Change
private static class IBiLink
to
protected static class IBiLink
notice that when compiling, EnclosingClass$1.class is not created.
why does protecting the class did not generate a synthetic class ?
Simply because when protecting the class, you implicitely get access to each of the super classes.
Why don't eclipse compiler generate a synthetic class ?
Eclipse use it built-in compiler, which you can configure it severity level.
By default, Access to a non-accessible member of an enclosing type is set to ignore as you can see on this image.
Change it for example to warning and you will get the following message.
which let me believe that eclipse, altought does not create an other class, will emulate it to simulate the synthetic member.

Why prefer the indirect generic's import to the actual class?

Using eclipse if I write this interface in the package mypack:
package mypack;
public interface MyInterface<A>{
public interface Test{
void sayHi();
}
}
And if I write this class in no package.
public class Test implements mypack.MyInterface<mypack.MyInterface.Test> {
private Test test = new Test();
}
Eclipse trigger me an error at compile-time, that I must implement the method sayHi().
I see no way out!
If I Ctrl+LMB to the type of the field test it takes me to the Class.
Bug reported
A small bug is reported here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=488077
What is happening here is
Test test = new Test();
the Test is being taken as a nested-type of the MyInterface you inherit from.
I will look into the JLS to see if there is a reason it chooses the inherited class over it's own name.
Note: MyInterface doesn't have to be generic. A simpler form of this problem is
interface MyInterface {
interface Test {
}
}
class Test extends MyInterface {
Test test = new Test(); // thinks this is the MyInterface.Test
}
BTW: As this is very confusion combination of class structure and names, I suggest you never do this in reality.
A note from JLS 7.4.2
Unnamed packages are provided by the Java SE platform principally for convenience when developing small or temporary applications or when just beginning development.

How to implement something similar to the #Override java annotation?

With this jdk code in ../java/lang/Override.java,
package java.lang;
import java.lang.annotation.*;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.SOURCE)
public #interface Override {
}
having just annotation declaration, java compiler is intelligent enough to detect error(compile time):
The method toString123() of type Example must override or implement a supertype method
in the below problem code.
package annotationtype;
public class Example {
#Override public String toString() {
return "Override the toString() of the superclass";
}
#Override public String toString123() {
return "Override the toString123() of the superclass";
}
public static void main(String[] args) {
}
}
Annotation declaration for Override just gets compiled to,
interface java.lang.Override extends java.lang.annotation.Annotation{
}
which is nothing more than an interface.
So,
How does interface java.lang.Override syntax help java compiler to detect above error at compile time?
The implementation that triggers the compile error doesn't lie in the annotation, it lies in the Java compiler.
If you want to write your own similar annotation processor, you would use the annotation processor API: http://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/Processor.html
which is nothing more than an interface.
So,
How does interface java.lang.Override syntax help java compiler to
detect above error at compile time?
That's right. Override is nothing more than an interface. The actual work is done by the java compiler. How the compiler does this is not specified.
Here are some links that explain how to work with an AnnotationProcessor to implement something similar to #Override :
Processor Java doc
Java annotation processing tool
Code generation using AnnotationProcessor
Annotation Processor, generating a compiler error
Source code analysis using Java 6 API
Playing with Java annotation processing

Overriding a method with generic Map parameter

I have following code:
class Key{
--Some Implementation
}
class A{
public void grpMap(ConcurrentMap<Key,List<Key> keyList){
--Some Implementation
}
}
public class B extends A{
--Edited
#Override -- [Made "O" capital case after kocko's reply]
public void grpMap(ConcurrentMap<Key,List<Key> keyList){
--Some Implementation
}
}
With above code i get following error in class B
The method grpMap(ConcurrentMap) of type B must override
or implement a supertype method
My problem is i can't change the way classes key, B and C are declared as these are legacy classes.
Any suggestion on how to get rid of this error?
EDIT---
JDK version used is 1.6.43
I am using eclipse which will auto generate annotations.
The annotation
#override
should be
#Override
↑
Also, check you Java version in the IDE - it should be 1.6, or newer, in order to get rid of the error.
Open Project properties -> Java compiler -> Set compliance level to 1.6 -> OK.

Dynamically loading classes which adhere to an interface

I have several classes which implement two interfaces. All of them implement the BaseInterface and some other interface which is specific to them.
I want to be able to use the loadClass method below to instantiate classes which are referred to in a .properties file and call the common method they all contain (because they implement BaseInterface).
public interface BaseInterface {
public void doBase();
}
public interface SpecificInterface extends BaseInterface {
public void doSpecific();
}
public class SpecificClass implements SpecificInterface {
public void doBase() { ... }
public void doSpecific() { ... }
}
public class LoadClass() {
private PropertiesLoader propertiesLoader = new PropertiesLoader();
public <C extends BaseInterface> C loadClass(String propertyName) {
Class<C> theClass;
// Load the class.
theClass = propertiesLoader.getPropertyAsClass(propertyName);
// Create an instance of the class.
C theInstance = theClass.newInstance();
// Call the common method.
theInstance.doBase();
return theInstance;
}
}
Unfortunately, when I run the code:
loadClassInstance.loadClass("SpecificClass");
I get the following exception:
Exception in thread "main" java.lang.ClassCastException:
SpecificClass cannot be cast to BaseInterface
at LoadClass.loadClass
Any ideas how I would solve this issue?
Many Thanks, Danny
Java's Service Provider Interface (SPI) libraries allow you to load classes with public parameterless constructors dynamically based on the interfaces they implement, and it's all done through the use of META-INF/services.
First, you'll need the interface:
package com.example;
public interface SomeService {
String getServiceId();
String getDisplayName();
}
Then when you need them, you can load them using Java's ServiceLoader class, which implements Iterable:
ServiceLoader<SomeService> loader = ServiceLoader.load(SomeService.class);
for (SomeService serv : loader) {
System.out.println(serv.getDisplayName());
}
Then when you have 1 or more implementing classes on your classpath, they register themselves in META-INF/services. So if you have the implementation:
package com.acme;
public class SomeImplementation implements SomeService {
// ...
public SomeImplementation() { ... }
// ...
}
Note that this class needs a default no-args constructor, this is not optional.
You register it with the class loader by creating a file in META-INF/services in your classpath (such as in the root of your jar) with the following properties:
The name of the file is the fully qualified class name of the interface, in this case, it's com.example.SomeService
The file contains a newline-separated list of implementations, so for the example implementation, it would contain one line: com.acme.SomeImplementation.
And there you go, that's it. How you build your project will determine where you put the META-INF/services stuff. Maven, Ant, etc. all have ways of handling this. I recommend asking another question about your specific build process if you have any trouble adding these files to your build.
If you replace your code with below it works. I doubt that PropertiesLoader is doing something that is not supposed to be done.
Class<?> theClass;
// Load the class.
theClass = Class.forName("SpecificClass");
// Create an instance of the class.
C theInstance = (C) theClass.newInstance();
BaseInterface base = loadClass();//There is no problem in casting
Java program normally is loaded by system classloader. The classes which are referred to in a .properties file are loaded by a user-defined classloader. Classes loaded by different classloaders are considered different even if have same name and are loaded from same classfile. In your case, the interface BaseInterface loaded by system classloader is different from the BaseInterface loaded by
PropertiesLoader.
To fix this, PropertiesLoader should delegate loading of BaseInterface to system classloader. Typical way to do so is to use system classloader as a parent classloader for PropertiesLoader.

Categories

Resources