0

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

  • Have you defined your services to be registerd as per https://docs.oracle.com/javase/tutorial/ext/basics/spi.html#register-service-providers? – hotzst May 15 '16 at 20:11
  • @hotzst, Indeed I have. I know it is loaded since, in the for loop, I can do "class.getName()" and it'll give me "anotherstuff.NotAbstractClass". – Pedro Almada May 15 '16 at 20:14

2 Answers2

0

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.

  • 1
    That seems logical and it's probably the case. However, "some class" is a bit vague in my mind. Neither TheAbstractClass nor the NotAbstractClass are modified in runtime, unless I'm misunderstanding something? – Pedro Almada May 15 '16 at 22:42
0

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