I have interfaces extending other interfaces. When the implementations of those interfaces are used, my IDE (Eclipse and IntelliJ idea) is unable to resolve the implementing class. This does not prevent the code from compiling, but it's very peculiar that the IDE is so uncertain. Is there something fundamental I am misunderstanding? I present a simplified structure that outlines the problem.
A base interface
public interface Avoidable {
public void avoid();
}
An interface that extends the base one
public interface MostlyAvoidable extends Avoidable {
public void most();
}
It's implementation
public class MostlyAvoidableImpl implements MostlyAvoidable {
#Override
public void most() {}
#Override
public void avoid() {}
}
A second extending interface
public interface SomewhatAvoidable extends Avoidable {
public void somewhat();
}
And its implementation
public class SomewhatAvoidableImpl implements SomewhatAvoidable {
#Override
public void avoid() {}
#Override
public void somewhat() {}
}
Finally a class that uses one of these implementations:
public class UsesSomewhatAvoidable {
private SomewhatAvoidable somewhatAvoidable;
public UsesSomewhatAvoidable(SomewhatAvoidable somewhatAvoidable) {
this.somewhatAvoidable = somewhatAvoidable;
}
public void someMethod() {
somewhatAvoidable.avoid();
}
}
Now, if I navigate to the somewhatAvoidable.avoid(); line and ask the IDE to find the code that implements the avoid() method, it asks me if I mean MostlyAvoidableImpl or SomewhatAvoidableImpl. Why? Surely it can figure this out?
This is what happens in Idea 10.5 when pressing Ctrl+Alt+B (admittedly quite an old version now), but it also happens in the latest version of Eclipse.
Edit: corrected typo in last line - SimplyAvoidImpl to SomewhatAvoidableImpl, and added screenshot.
I took all your interfaces and classes and set it up in my own IntelliJ and I don't have the problem you describe.
Position the caret at the avoid method in UsesSomewhatAvoidable and then press Ctrl+Alt+B (Go To | Implementation(s)):
And I end up at the implementation in SomewhatAvoidableImpl:
Surely it can figure this out?
Yes, it should be able to figure out that the avoid() method executes using the interface given by SomewhatAvoidable. If it's giving a completely different class (such as MostlyAvoidableImpl in this case) then as far as I can see that would be a bug (with the example you've given anyway, I can't see how SomewhatAvoidable could possibly relate to MostlyAvoidableImpl.) It's possible you've confused the IDE somehow - try cleaning or restarting and see if it makes a difference.
However, it may validly give you an option to select an implementation more specifically, since this cannot be determined by static analysis (but obviously one such implementation has to be selected for the program to work correctly at runtime.)
From the code you have given, somewhatAvoidable (in the UsesSomewhatAvoidable class) is declared as a SomewhatAvoidable which is an interface and doesn't have an implementation for the avoid() method. The compiler (and thus the IDE) cannot know which class you want to jump to for the implementation.
Related
The simplest code to demonstrate the issue is this:
Main interface in Kotlin:
interface Base <T : Any> {
fun go(field: T)
}
Abstract class implementing it and the method:
abstract class Impl : Base<Int> {
override fun go(field: Int) {}
}
Java class:
public class JavaImpl extends Impl {
}
It should work, but it doesn't. The error is
Class 'JavaImpl' must either be declared abstract or implement abstract method 'go(T)' in 'Base'
If the JavaImpl class was in Kotlin, it would work. Also if the T was cast to String or Integer or any object, it would work too. But not with Int.
Is there any clever solution apart from using Integer and suppressing hundreds of warnings in Kotlin subclasses?
Update: created the issue.
Looking at the byte code we can see, that the Impl-class basically has produced the following function:
public go(I)V
where the parameter is a primitive integer. Also a synthetic bridge-function (go(Object)) is generated, which however would also be generated on the Java-side for such generic functions.
On the Java side however it doesn't suffice to have something like public void go(int field) in place. Now we need that go(Integer field)-function, which isn't present.
For me that sound's like an interop-problem that should probably be reported and linked back here again. Actually having had some time to investigate, there seem to be some issues already: KT-17159 and KT-30419, but also KT-5128 seem to relate to this problem. The kotlin compiler knows how to deal with this and doesn't need any further information about it in the class-file (i.e. the implementing Kotlin class knows, that it doesn't need to implement something like fun go(field : Int?)). For the Java-side such counterpart does not exist. I wonder whether this could even be fixed nicely with the compiler/byte-code or whether this will remain a specific interop-problem.
Some workarounds to deal with that problem (in case this is deliberate and not a real problem):
Add an additional function as follows to Impl:
fun go(field : Int?) = go(field ?: error("Actually the given field should never be null"))
// or simply:
fun go(field : Int?) = go(field!!)
That way you would not need to implement it. However then you would also expose that nullable function to the Kotlin side, which you probably don't want.
For that specific purpose it may seem more convenient to do it the other way around:
declare the class and the interface in Java and use it on the Kotlin side. That way you could still declare something like
abstract class KotlinClass : JavaInterface<Int> {
override fun go(field : Int) { // an IDE might suggest you to use Int? here...
// ...
}
}
You can use javap to analyze the problem, showing members of compiled interface and classes.
javap Base
public interface Base<T> {
public abstract void go(T);
}
javap Impl
public abstract class Impl implements Base<java.lang.Integer> {
public void go(int);
public void go(java.lang.Object);
public Impl();
}
So, the problem is exactly that pointed out by #Roland: in order to satisfay the contract requested by the Base interface, the Java compiler needs a public void go(java.lang.Integer) method but the method generated by Kotlin compiler has int as parameter.
If you implement the interface in Java, with something like
class JI implements Base<Integer> {
#Override
public void go(#NotNull Integer field) {
}
}
you can analyze its compiled version with javap obtaining
javap JI
class JI implements Base<java.lang.Integer> {
org.amicofragile.learning.kt.JI();
public void go(java.lang.Integer);
public void go(java.lang.Object);
}
So, if you plan to use Kotlin class Impl as superclass of Java classes, the solution is simply to use <Integer>, not <Int>, as type parameter: Int is a Kotlin class, translated to int by the compiler; Integer is the Java class you usually use in Java code.
Changing your example to
abstract class Impl : Base<Integer> {
override fun go(field: Integer) {}
}
public class JavaImpl extends Impl {
}
the JavaImpl Java class compiles without errors.
Now I'm trying to implement custom rule usin SonarQube for the next case. For example I have next class:
public class SuperClass{
#CannotOverrideAnnotation
protected void methodCanNotBeOverrided(){}
protected void methodCanBeOverrided(){}
}
And I'd like to detect cases where somebody tries to override methodCanNotBeOverrided.
public class ChildClass extend SuperClass{
protected void methodCanNotBeOverrided(){} // Noncompliant
protected void methodCanBeOverrided(){} //Compliant
}
Firstable, I think I have to visit every MethodTree and get owner of it like as:
#Override
protected void visitNode(MethodTree method){
Tree declaration = method.symbol().owner().declaration();
if (declaration.is(Tree.Kind.CLASS)) {
TypeTree superClass = ((ClassTree) declaration).superClass();
}
}
Ok, I've got superClass and I can extract fullyQualifiedName and use Reflection API.... but maybe there is other way to do it via SonarQube API?
I've found out that RetentionPolicy for this annotations equals to SOURCE, it means that I have no idea now how to implement rule, Reflection API cannot help me... Could anybody help me with ideas about this stuff.
Also I've found out that there is a very useful method in JavaSymbol.MethodJavaSymbol such as overriddenSymbol but this class doesn't belong org.sonar.plugins.java.api package and according to link I cannot use it.
org.sonar.plugins.java.api package containts the similar interface Symbol, but there is only common methods for all languages and there're not specific methods for Java.
I will be great for any help.
P.s. I'd like to emphasize that SuperClass - is an external library. I don't have an opportunity to change something there.
I am trying to make a Java app that can load plugins implementing an abstract class and am having an AbstractMethodError with the instances generated from ServiceLoader. The code is a bit heavy so I've made a simplification below.
First, I have an abstract class:
package stuff.TheAbstractClass;
public abstract class TheAbstractClass implements ClassInterface{
//Stuff happens
}
Which implements the below interface:
package stuff.ClassInterface;
public interface ClassInterface {
public String getClassName();
}
I have a service provider NotAbstractClass which extends TheAbstractClass and states so in a meta-inf/services folder:
package anotherstuff.NotAbstractClass;
public final class NotAbstractClass extends TheAbstractClass implements ClassInterface{
private String name = "Silent Bob";
#Override
public String getClassName() { return name; }
}
Then on the main application (which is actually a plugin inside another application), I want to find all classes which extend TheAbstractClass:
package stuff.TheApp;
import java.util.ServiceLoader;
public class TheApp {
private String name;
public final static TheApp INSTANCE = new TheApp();
private TheApp() {
ServiceLoader<TheAbstractClass> serviceLoader =
ServiceLoader.load(TheAbstractClass.class);
for (TheAbstractClass class: serviceLoader) {
name = class.getClassName;
}
}
My application does find NotAbstractClass. I know this since, in the for loop, I can do class.getName() and it'll give me anotherstuff.NotAbstractClass) but gives the error:
java.lang.AbstractMethodError: stuff.TheAbstractClass.getClassName()Ljava/lang/String;
I'm stumped. Any suggestion? Thank you, Pedro
According to the API for AbstractMethodError you get this:
Thrown when an application tries to call an abstract method. Normally,
this error is caught by the compiler; this error can only occur at run
time if the definition of some class has incompatibly changed since
the currently executing method was last compiled.
Just by looking at your code and your comment I see that this could only have happened at runtime.
If that is the case then:
some class has incompatibly changed since the currently executing
method was last compiled
I've tested your logic after some adjustments in a Java compatible form and I had no problems. The only thing that seems to be happening is a change in any of the subclasses of TheAbstractClass.
Another thing I did was to declare the dependencies using the dependency files in: resources/META-INF/services:
file: <full-package>.TheAbstractClass
content: <full-package>.NotAbstractClass
After this I had no problems.
It seems the issue wasn't in the code, but in the IDE (IntelliJ). I deleted all previously packaged jars and made new jars without changing anything and it magically worked... So it's an IDE bug, and not a language issue!
Thanks to #Joao and #hotzst for taking time to read however.
Best, Pedro
i have two independent projects Basic and Extension with following setup
Project A:
class Handler {
public void handle(){
...
}
}
Project B
import Handler; //from Proejct A
class SomeClass{
someMethod() {
handle(); //dependency to Project As class with handle method
}
}
So the problem is the dependecy to the handle method which exists at Project A but not at compile time on Project B.
The final step is to have build Project Extension as a jar and import it inside Project Basic.
Ofc the compiler will give me error when i build Project B since the handle is not known at compile time.
For this issue i need a solution:
Either: Tell java that the missing code (import class with handle method) will be there at running time.
Or maybe Dependency Injection due to a factory pattern.
I am known to the factory pattern, but i don't understand how it could help me in this situation.
Or another solution.
Can you help me?
Neither of these are valid Java - won't compile. The proper keyword is "class", not "Class".
You have to provide it at compile time once you get it right - you have no choice. No way around it.
Maybe you should look at the Java JDK and follow the example in the java.sql package: Interfaces. Connection, ResultSet, Statement, etc. are all interfaces so vendors can provide their own implementations. Users only deal with interfaces.
Your GenericHandler should be an interface that you provide to clients. They add their implementations and add their JAR file containing the custom implementation at runtime.
Basic interface that all extensions implement:
public interface GenericHandler {
void genericHandle();
}
Extension code:
import GenericHandler;
public class Extension implements GenericHandler {
public void genericHandle() {
// Do something useful here
}
}
The factory pattern works only if you provide a finite, closed set of implementations:
public class GenericHandlerFactory {
private final GenericHandlerFactory instance = new GenericHandlerFactory();
private GenericHandlerFactory() {}
public GenericHandler getInstance() { return this.instance; }
public GenericHandler createHandler(Class genericHandlerClass) {
GenericHandler result = null;
// Code to create the GenericHandler you want.
return result;
}
}
If users can extend your interface without your knowledge then a factory can't work; you have to stick to the JDBC example.
I need to extend a java software package for a new algorithm. I've tested my changes by editing some of the classes in the package. Now I want to write extension classes that extend the original classes in the package and keep the main code intact. Changes are easy when I am adding methods to the main code.
But what should I do in this case?
class origin
{
public a_method()
{
//Some code
}
}
class example extends origin
{
public a_method()
{
//Everything in the original method but very small changes
}
}
Thanks for helping an electrician to learn java
Use the #Override annotation like so:
class Example extends Origin {
#Override
public a_method() {
//Everything in the original method but very small changes
}
}
I would recommend keeping the same method parameters and return values, lest things start to break.