Dynamically load classes that adhere to the interface

I have several classes that implement two interfaces. They all implement BaseInterface and some other interface that is specific to them.

I want to be able to use the loadClass method below to instantiate classes that are mentioned in the .properties file and call the generic method that they all contain (since 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 can solve this problem?

Thanks a lot, Danny

+4
source share
3 answers

The Java Service Provider Interface (SPI) libraries let you dynamically load classes with open parameter-free constructors based on the interfaces that they implement, and all this is done using META-INF/services .

First you need an interface :

 package com.example; public interface SomeService { String getServiceId(); String getDisplayName(); } 

Then, when you need them, you can load them using the Java 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 class path, they are registered with META-INF/services . So, if you have an implementation:

 package com.acme; public class SomeImplementation implements SomeService { // ... public SomeImplementation() { ... } // ... } 

Note that this class needs a default constructor with no arguments; this is optional.

You register it in the classloader, creating a file in META-INF/services in your classpath (for example, in the root of your jar) with the following properties:

  1. The file name is the fully qualified name of the interface class, in this case com.example.SomeService
  2. This file contains a list of implementations, separated by a newline, so for an example implementation it will contain one line: com.acme.SomeImplementation .

And here you are. How you create your project will determine where you put the META-INF/services material. Maven, Ant, etc. Everyone has ways to handle this. I recommend asking another question about your specific build process if you have problems adding these files to your build.

+13
source

If you replace your code below, it will work. I doubt that PropertiesLoader does what should not 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 
+1
source

A Java program is usually loaded by the system loader. Classes that are referenced in the .properties file are loaded by the custom class loader. Classes loaded by different class loaders are considered different, even if they have the same name and are loaded from the same class file. In your case, the BaseInterface interface loaded by the system class loader is different from the BaseInterface loaded by PropertiesLoader. To fix this, PropertiesLoader must delegate the loading of BaseInterface to the system bootloader. A typical way to do this is to use the system class loader as the parent class loader for PropertiesLoader.

0
source

Source: https://habr.com/ru/post/1437817/


All Articles