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:
com.example.SomeService
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 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