I have a java jar file contain interface named IOperator like this :
public interface IOperator {
String execute(Map<String, String> parameters);
boolean isSuccess();
}
now in groovy script (in single file):
public class ClassA {
}
public class ClassB {
}
public class classC implements IOperator {
#Override
String execute(Map<String, String> parameters) {
ClassA classA = new ClassA();
ClassB classB = new ClassB();
return null
}
#Override
boolean isSuccess() {
return false
}
}
Is there any way scan this groovy script to find that specific class implemented IOperator and invoke execute method ?
Note: I want do it in Java code.
Finally i found answer :
public class OperatorManager {
public static void run(File scriptFile) {
try {
GroovyScriptEngineImpl engine = (GroovyScriptEngineImpl) new GroovyScriptEngineFactory().getScriptEngine();
GroovyClassLoader classLoader = new GroovyClassLoader();
classLoader.parseClass(new GroovyCodeSource(scriptFile));
engine.setClassLoader(classLoader);
Class<?>[] loadedClasses = engine.getClassLoader().getLoadedClasses();
Class<?> operatorImplClass = Arrays.stream(loadedClasses)
.filter(IOperator.class::isAssignableFrom)
.findFirst().orElse(null);
if (operatorImplClass != null) {
String result = invokeMethod(operatorImplClass);
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static String invokeMethod(Class<?> item) {
try {
Object instance = item.getConstructor().newInstance();
Method execute = item.getDeclaredMethod("execute", Map.class);
return (String) execute.invoke(instance, new Object[]{null});
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Related
I am trying to develop a dynamic Factory Class, where Factory Class does not know the factories, I will put the code so that the question is clearer.
App.java:
package br.com.factory;
public class App {
public static void main(String[] args) {
Product product = ProductFactory.createProduct("Xiaomi", "MI XX");
if (product != null) {
System.out.println(product.getName());
}
}
}
Product.java:
package br.com.factory;
public interface Product {
public String getName();
}
ProductFactory.java:
package br.com.factory;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class ProductFactory {
private static HashMap<String, Map<String, Supplier<Product>>> registries = new HashMap<>();
private ProductFactory(){}
static {
ClassLoaderInitializer.initialize(ProductSupplier.class);
}
public static Product createProduct(String manufacturer, String model) {
Map<String, Supplier<Product>> manufacturers = registries.getOrDefault(manufacturer, null);
if (manufacturers != null){
Supplier<Product> suppliers = manufacturers.getOrDefault(model, null);
if (suppliers != null) {
return suppliers.get();
}
}
return null;
}
public static void registerFactory(String manufacturer, String model, Supplier<Product> supplier) {
registries
.computeIfAbsent(manufacturer, p -> new HashMap<>())
.putIfAbsent(model, supplier);
}
}
ProductSupplier.java:
package br.com.factory;
import java.util.function.Supplier;
public interface ProductSupplier extends Supplier<Product> {
}
XiaomiFactory.java:
package br.com.factory.xiaomi;
import br.com.factory.Product;
import br.com.factory.ProductFactory;
import br.com.factory.ProductSupplier;
public class XiaomiFactory implements ProductSupplier {
static {
ProductFactory.registerFactory("Xiaomi", "MI XX", XiamoMiXX::new);
}
private XiaomiFactory() {
}
#Override
public Product get() {
return new XiamoMiXX();
}
}
class XiamoMiXX implements Product {
#Override
public String getName() {
return "Xiaomi Mi XX";
}
}
ClassLoaderInitializer.java:
package br.com.factory;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
public class ClassLoaderInitializer {
private ClassLoaderInitializer() {
}
public static void initialize(Class<?> parentClass) {
try {
Enumeration<URL> resources = ClassLoaderInitializer.class.getClassLoader().getResources("");
while (resources.hasMoreElements()) {
URL nextElement = resources.nextElement();
try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { nextElement });) {
URL[] urLs = urlClassLoader.getURLs();
for (URL u : urLs) {
try {
File file = new File(u.toURI());
initializeClass(file, file, urlClassLoader, parentClass);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void initializeClass(File root, File file, ClassLoader loader, Class<?> parentClass) {
if (file.isDirectory()) {
File[] listFiles = file.listFiles();
for (File f : listFiles) {
initializeClass(root, f, loader, parentClass);
}
} else {
if (file.getName().toUpperCase().endsWith(".class".toUpperCase())) {
try {
String fileName = file.toString();
String className = fileName.substring(root.toString().length() + 1,
fileName.toUpperCase().lastIndexOf(".class".toUpperCase())).replace(File.separator, ".");
Class<?> clazz = Class.forName(className, false, loader);
if (clazz.isAssignableFrom(parentClass)) {
Class.forName(className, true, loader);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
The problem occurs in the initializeClass method,
more precisely in:
Class <?> Clazz = Class.forName (className, false, loader);
if (clazz.isAssignableFrom (parentClass)) {
Class.forName (className, true, loader);
}
the idea of the ClassLoaderInitializer class is to initialize the classes that inherit from a given class "Class <?> parentClass"
but when I call the method
Class.forName (className, true, loader);
for the second time, passing true in the second parameter, the class is not initialized.
if I call:
Class.forName (className, true, loader);
directly, the initializeClass method will initialize all classes, what I would not like to happen.
is there any outside of me explicitly initializing (forcing) the class specifies?
Use ServiceLoader
ServiceLoader allows you to register services using metadata in the META-INF/services/ folder. It allows you to use a Java API to register all your services at once. It's better than trying to do it yourself, especially since it's standardized and doesn't require "magic" to get registered. Magic which might be broken in Java 9 and later with the introduction of modules.
This is how you should use it:
ProductSupplier.java
public interface ProductSupplier extends Supplier<Product> {
// Add these methods
String getManufacturer();
String getModel();
}
ProductFactory.java
public class ProductFactory {
private static final Map<List<String>, ProductSupplier> SUPPLIERS;
static {
var suppliers = new HashMap<List<String>, ProductSupplier>();
for (var supplier : ServiceLoader.load(ProductSupplier.class)) { // Yes, it's as easy as this.
var key = List.of(supplier.getManufacturer(), supplier.getModel());
suppliers.put(key, supplier);
}
SUPPLIERS = Map.copyOf(suppliers);
}
public static Product createProduct(String manufacturer, String model) {
var key = List.of(manufacturer, model);
var supplier = suppliers.getOrDefault(key, () -> null);
return supplier.get();
}
}
XiaomiFactory.java
public class XiaomiFactory implements ProductSupplier {
#Override public String getManufacturer() { return "Xiaomi"; }
#Override public String getModel() { return "Mi XX"; }
#Override public Product get() { return new XiaomiMiXX(); }
}
In META-INF/services/com.br.factory.ProductSupplier:
com.br.factory.xiaomi.XiaomiFactory
com.br.factory.samsung.SamsungFactory # Need to create
com.br.factory.apple.AppleFactory # Need to create
I have a working annotation processor that gathers information of the annotated classes. Everything is there during compilation. But I would like to have access to those results during runtime.
#SupportedSourceVersion(SourceVersion.RELEASE_8)
#AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {
private final static List<TestInfo> tests = new ArrayList<>();
#Override
public Set getSupportedAnnotationTypes() {
return new LinkedHashSet() {
{
add(Annotation.class.getCanonicalName());
}
};
}
#Override
public boolean process(final Set<? extends TypeElement> annotations,
final RoundEnvironment env) {
System.out.println("Processing!");
if (!env.processingOver()) {
Set<? extends Element> rootE = env.getRootElements();
for (Element e : rootE) {
if (e.getKind() == ElementKind.CLASS) {
TestInfo t = new TestInfo(e.asType().toString());
for (Element se : e.getEnclosedElements()) {
if (se.getKind() == ElementKind.METHOD) {
t.addMethod(se.getSimpleName().toString());
}
}
getTests().add(t);
}
}
getTests().forEach(ti -> {
System.out.println(ti);
});
}
return false;
}
public static TypeElement findEnclosingTypeElement(Element e) {
while (e != null && !(e instanceof TypeElement)) {
e = e.getEnclosingElement();
}
return TypeElement.class.cast(e);
}
/**
* #return the tests
*/
public static List<TestInfo> getTests() {
return tests;
}
}
Is there a way to retrieve the results at runtime? TestProcessor.getTests returns an empty list.
Here's the TestInfo class fyi:
public class TestInfo {
private final String name;
private final List<String> methods = new ArrayList<>();
public TestInfo(String name) {
this.name = name;
}
public void addMethod(String m) {
getMethods().add(m);
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #return the methods
*/
public List<String> getMethods() {
return methods;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name).append(methods.toString());
return sb.toString();
}
}
Update: The annotation is marked with retention runtime.
The annotation-processing is in compile time. So you can't get the information in the runtime.
A direct way is to write the information as a resource file in compile time and read it at runtime.
Here is my example:
The annotation:
#Retention(SOURCE)
#Target(TYPE)
public #interface Anno {
}
The processor:
#Override
public boolean processActual(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
try {
write(roundEnv);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private void write(RoundEnvironment roundEnv) throws IOException, UnsupportedEncodingException {
Filer filer = processingEnv.getFiler();
FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT, "", "TestInfo");
OutputStream output = resource.openOutputStream();
PrintStream writer = new PrintStream(output, false, "UTF-8");
roundEnv.getElementsAnnotatedWith(Anno.class)
.stream()
.filter(e -> e.getKind() == ElementKind.CLASS)
.map(e -> e.asType().toString())
.forEach(writer::println);
writer.flush();
}
And the user code:
#Anno
public class Q48177784 {
public static final List<Class<?>> CLASSES;
static {
try {
URL resource = Q48177784.class.getClassLoader().getResource("TestInfo");
CLASSES = Files.readAllLines(Paths.get(resource.toURI()))
.stream()
.map(s -> {
try {
return Class.forName(s);
} catch (ClassNotFoundException e) {
throw new Error(e);
}
})
.collect(Collectors.toList());
} catch (Exception e) {
throw new Error(e);
}
}
public static void main(String[] args) {
System.out.println(CLASSES);
}
}
After build with processor, run the main method:
[class xdean.stackoverflow.Q48177784]
For your case, the only thing you should do is serialize/deserialize your TestInfo
Check out #RetentionPolicy. I think you want to set it to RUNTIME.
I've been reading about the new Architecture Components that were introduced to Android and I cannot figure out how this works:
ViewModelProviders.of(Activity).get(Class)
Initially I thought that it calls the default constructor and returns a ViewModel object that you then instantiate with eg. an init() method as per
public class UserProfileViewModel extends ViewModel {
private String userId;
private User user;
public void init(String userId) {
this.userId = userId;
}
public User getUser() {
return user;
}
}
Snippet taken from the guide: https://developer.android.com/topic/libraries/architecture/guide.html
However, later on in the guide there is this snippet:
public class UserProfileViewModel extends ViewModel {
private LiveData<User> user;
private UserRepository userRepo;
#Inject // UserRepository parameter is provided by Dagger 2
public UserProfileViewModel(UserRepository userRepo) {
this.userRepo = userRepo;
}
public void init(String userId) {
if (this.user != null) {
// ViewModel is created per Fragment so
// we know the userId won't change
return;
}
user = userRepo.getUser(userId);
}
So how does the ViewModelProvider know to call the provided constructor? Or it sees that there is only 1 constructor and calls that? For example if there were 2 constructors what would happen?
I tried digging through the code and what I found was:
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
Inside of the DefaultFactory class inside ViewModelProviders.java. However, this confused me even further. How does getConstructor(Application.class) even work when ViewModel objects do not have a constructor taking an Application as an argument?
In the snippet there is a condition that checks if modelClass is of type AndroidViewModel (inherits ViewModel) which constructor takes Application parameter. This is more like exclusive case that spares the Factory of looking up for constructor matching specific parameters.
This provider looks up for constructor matching provider arguments when creating it:
public class ViewModelParameterizedProvider {
private AtomicBoolean set = new AtomicBoolean(false);
private ViewModelStore viewModelStore = null;
static ViewModelParameterizedProvider getProvider() {
return new ViewModelParameterizedProvider();
}
#MainThread
public static ViewModelProvider ofSupportFragment(Fragment fragment, Object... params) {
return getProvider().of(fragment).with(params);
}
#MainThread
public static ViewModelProvider ofActivity(FragmentActivity fragmentActivity, Object... params) {
return getProvider().of(fragmentActivity).with(params);
}
#MainThread
public static ViewModelProvider ofFragment(android.app.Fragment fragment, Object... params) {
return getProvider().of(fragment).with(params);
}
private ViewModelParameterizedProvider of(Fragment fragment) {
checkForPreviousTargetsAndSet();
viewModelStore = ViewModelStores.of(fragment);
return this;
}
private ViewModelParameterizedProvider of(android.app.Fragment fragment) {
FragmentActivity fragAct = (FragmentActivity) fragment.getActivity();
return of(fragAct);
}
private ViewModelParameterizedProvider of(FragmentActivity activity) {
checkForPreviousTargetsAndSet();
viewModelStore = ViewModelStores.of(activity);
return this;
}
private ViewModelProvider with(Object... constructorParams) {
return new ViewModelProvider(viewModelStore, parametrizedFactory(constructorParams));
}
private void checkForPreviousTargetsAndSet() {
if (set.get()) {
throw new IllegalArgumentException("ViewModelStore already has been set. Create new instance.");
}
set.set(true);
}
private ViewModelProvider.Factory parametrizedFactory(Object... constructorParams) {
return new ParametrizedFactory(constructorParams);
}
private final class ParametrizedFactory implements ViewModelProvider.Factory {
private final Object[] mConstructorParams;
ParametrizedFactory(Object... constructorParams) {
mConstructorParams = constructorParams;
}
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass == null) {
throw new IllegalArgumentException("Target ViewModel class can not be null")
}
Log.w("ParametrizedFactory", "Don't use callbacks or Context parameters in order to avoid leaks!!")
try {
if (mConstructorParams == null || mConstructorParams.length == 0) {
return modelClass.newInstance();
} else {
Class<?>[] classes = new Class<?>[mConstructorParams.length];
for (int i = 0; i < mConstructorParams.length; i++) {
classes[i] = mConstructorParams[i].getClass();
}
return modelClass.getConstructor(classes).newInstance(mConstructorParams);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
}
Here is kotlin version.
Here is more read on the subject
I want to do a factory pattern in java with generics. My code is:
The interface:
public abstract class Factory<T> {
public abstract T create();
}
FactoryA class:
public class FactoryA extends Factory<FactoryA> {
public FactoryA() {
}
public FactoryA create() {
return new FactoryA();
}
}
FactoryB class:
public class FactoryB extends Factory<FactoryB> {
public FactoryB() {
}
public FactoryB create() {
return new FactoryB();
}
}
The main class:
public class FactoryCreator {
public static <T> T createFactory() {
Factory<T> t = ?; // is that right way?
return t.create();
}
public static void main(String[] args) {
FactoryA factoryA = FactoryCreator.createFactory();
FactoryB factoryB = FactoryCreator.createFactory();
}
}
The question, what Factory t = need to be equal, or is there any other way?
Not really sure what you're trying to achieve, but this might help;
public interface Factory<T>
{
public T create(String type);
public T create(String type, Object arg);
public T create(String type, Object[] args);
}
And then have a class implement that factory interface, like this;
public class TemplateFactory<T> implements Factory {
#Override
public T create(String type) throws IllegalArgumentException
{
return create(type, null);
}
#Override
public T create(String type, Object arg) throws IllegalArgumentException
{
// Convert to array of 1 element
Object[] arguments = new Object[1];
arguments[0] = arg;
return create(type, arguments);
}
#Override
public T create(String type, Object[] args) throws IllegalArgumentException
{
// Create array for all the parameters
Class<?> params[] = (args != null) ? new Class<?>[args.length] : new Class<?>[0];
if(args != null)
{
// Adding the types of the arguments
for(int i = 0; i < args.length; ++i)
params[i] = (args[i] != null) ? args[i].getClass() : null;
}
try
{
// Create a class variable
Class classLoader = Class.forName(type);
// Find the right constructor
Constructor co;
if(params.length > 0)
co = classLoader.getConstructor(params);
else
co = classLoader.getConstructor();
// Instantiate the class with the given arguments
T newObject = (T)co.newInstance(args);
return newObject;
}
catch(Exception e)
{
throw new IllegalArgumentException(e.toString());
}
}
}
And then use it like this (using some imaginary strategy-classes as an example):
TemplateFactory<StrategyInterface> factory;
factory = new TemplateFactory<>();
factory.create("packageName.StrategyA");
factory.create("packageName.StrategyB");
factory.create("packageName.StrategyC");
The strategy classes (A, B and C) would implement the StrategyInterface-class in this example.
Something like this might work:
public static <T extends Factory> T createFactory(Class<T> clazz) {
try {
t = clazz.newInstance();
return t.create();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
...
FactoryA factoryA = FactoryCreator.createFactory(FactoryA.class);
alternatively, without parameters. But then you need two methods.
public static FactoryA createFactoryA() {
return new FactoryA().create();
}
...
FactoryA factoryA = FactoryCreator.createFactoryA();
Since the Generic types are erased at runtime you have to supply the Class parameter so that the runtime knows what class you are talking about.
I have to handle two classes with identical methods but they don't implement the same interface, nor do they extend the same superclass. I'm not able / not allowed to change this classes and I don't construct instances of this classes I only get objects of this.
What is the best way to avoid lots of code duplication?
One of the class:
package faa;
public class SomethingA {
private String valueOne = null;
private String valueTwo = null;
public String getValueOne() { return valueOne; }
public void setValueOne(String valueOne) { this.valueOne = valueOne; }
public String getValueTwo() { return valueTwo; }
public void setValueTwo(String valueTwo) { this.valueTwo = valueTwo; }
}
And the other...
package foo;
public class SomethingB {
private String valueOne;
private String valueTwo;
public String getValueOne() { return valueOne; }
public void setValueOne(String valueOne) { this.valueOne = valueOne; }
public String getValueTwo() { return valueTwo; }
public void setValueTwo(String valueTwo) { this.valueTwo = valueTwo; }
}
(In reality these classes are larger)
My only idea is now to create a wrapper class in this was:
public class SomethingWrapper {
private SomethingA someA;
private SomethingB someB;
public SomethingWrapper(SomethingA someA) {
//null check..
this.someA = someA;
}
public SomethingWrapper(SomethingB someB) {
//null check..
this.someB = someB;
}
public String getValueOne() {
if (this.someA != null) {
return this.someA.getValueOne();
} else {
return this.someB.getValueOne();
}
}
public void setValueOne(String valueOne) {
if (this.someA != null) {
this.someA.setValueOne(valueOne);
} else {
this.someB.setValueOne(valueOne);
}
}
public String getValueTwo() {
if (this.someA != null) {
return this.someA.getValueTwo();
} else {
return this.someB.getValueTwo();
}
}
public void setValueTwo(String valueTwo) {
if (this.someA != null) {
this.someA.setValueTwo(valueTwo);
} else {
this.someB.setValueTwo(valueTwo);
}
}
}
But I'm not realy satisfied with this solution. Is there any better / more elegant way to solve this problem?
A better solution would be to create an interface to represent the unified interface to both classes, then to write two classes implementing the interface, one that wraps an A, and another that wraps a B:
public interface SomethingWrapper {
public String getValueOne();
public void setValueOne(String valueOne);
public String getValueTwo();
public void setValueTwo(String valueTwo);
};
public class SomethingAWrapper implements SomethingWrapper {
private SomethingA someA;
public SomethingWrapper(SomethingA someA) {
this.someA = someA;
}
public String getValueOne() {
return this.someA.getValueOne();
}
public void setValueOne(String valueOne) {
this.someA.setValueOne(valueOne);
}
public String getValueTwo() {
return this.someA.getValueTwo();
}
public void setValueTwo(String valueTwo) {
this.someA.setValueTwo(valueTwo);
}
};
and then another class just like it for SomethingBWrapper.
There, a duck-typed solution. This will accept any object with valueOne, valueTwo properties and is trivially extensible to further props.
public class Wrapper
{
private final Object wrapped;
private final Map<String, Method> methods = new HashMap<String, Method>();
public Wrapper(Object w) {
wrapped = w;
try {
final Class<?> c = w.getClass();
for (String propName : new String[] { "ValueOne", "ValueTwo" }) {
final String getter = "get" + propName, setter = "set" + propName;
methods.put(getter, c.getMethod(getter));
methods.put(setter, c.getMethod(setter, String.class));
}
} catch (Exception e) { throw new RuntimeException(e); }
}
public String getValueOne() {
try { return (String)methods.get("getValueOne").invoke(wrapped); }
catch (Exception e) { throw new RuntimeException(e); }
}
public void setValueOne(String v) {
try { methods.get("setValueOne").invoke(wrapped, v); }
catch (Exception e) { throw new RuntimeException(e); }
}
public String getValueTwo() {
try { return (String)methods.get("getValueTwo").invoke(wrapped); }
catch (Exception e) { throw new RuntimeException(e); }
}
public void setValueTwo(String v) {
try { methods.get("setValueTwo").invoke(wrapped, v); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
You can use a dynamic proxy to create a "bridge" between an interface you define and the classes that conform but do not implement your interface.
It all starts with an interface:
interface Something {
public String getValueOne();
public void setValueOne(String valueOne);
public String getValueTwo();
public void setValueTwo(String valueTwo);
}
Now you need an InvocationHandler, that will just forward calls to the method that matches the interface method called:
class ForwardInvocationHandler implements InvocationHandler {
private final Object wrapped;
public ForwardInvocationHandler(Object wrapped) {
this.wrapped = wrapped;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Method match = wrapped.getClass().getMethod(method.getName(), method.getParameterTypes());
return match.invoke(wrapped, args);
}
}
Then you can create your proxy (put it in a factory for easier usage):
SomethingA a = new SomethingA();
a.setValueOne("Um");
Something s = (Something)Proxy.newProxyInstance(
Something.class.getClassLoader(),
new Class[] { Something.class },
new ForwardInvocationHandler(a));
System.out.println(s.getValueOne()); // prints: Um
Another option is simpler but requires you to subclass each class and implement the created interface, simply like this:
class SomethingAImpl extends SomethingA implements Something {}
class SomethingBImpl extends SomethingB implements Something {}
(Note: you also need to create any non-default constructors)
Now use the subclasses instead of the superclasses, and refer to them through the interface:
Something o = new SomethingAImpl(); // o can also refer to a SomethingBImpl
o.setValueOne("Uno");
System.out.println(o.getValueOne()); // prints: Uno
i think your original wrapper class is the most viable option...however it can be done using reflection, your real problem is that the application is a mess...and reflection is might not be the method you are looking for
i've another proposal, which might be help: create a wrapper class which has specific functions for every type of classes...it mostly copypaste, but it forces you to use the typed thing as a parameter
class X{
public int asd() {return 0;}
}
class Y{
public int asd() {return 1;}
}
class H{
public int asd(X a){
return a.asd();
}
public int asd(Y a){
return a.asd();
}
}
usage:
System.out.println("asd"+h.asd(x));
System.out.println("asd"+h.asd(y));
i would like to note that an interface can be implemented by the ancestor too, if you are creating these classes - but just can't modify it's source, then you can still overload them from outside:
public interface II{
public int asd();
}
class XI extends X implements II{
}
class YI extends Y implements II{
}
usage:
II a=new XI();
System.out.println("asd"+a.asd());
You probably can exploit a facade along with the reflection - In my opinion it streamlines the way you access the legacy and is scalable too !
class facade{
public static getSomething(Object AorB){
Class c = AorB.getClass();
Method m = c.getMethod("getValueOne");
m.invoke(AorB);
}
...
}
I wrote a class to encapsulate the logging framework API's. Unfortunately, it's too long to put in this box.
The program is part of the project at http://www.github.com/bradleyross/tutorials with the documentation at http://bradleyross.github.io/tutorials. The code for the class bradleyross.library.helpers.ExceptionHelper in the module tutorials-common is at https://github.com/BradleyRoss/tutorials/blob/master/tutorials-common/src/main/java/bradleyross/library/helpers/ExceptionHelper.java.
The idea is that I can have the additional code that I want to make the exception statements more useful and I won't have to repeat them for each logging framework. The wrapper isn't where you eliminate code duplication. The elimination of code duplication is in not having to write multiple versions of the code that calls the wrapper and the underlying classes. See https://bradleyaross.wordpress.com/2016/05/05/java-logging-frameworks/
The class bradleyross.helpers.GenericPrinter is another wrapper that enables you to write code that works with both the PrintStream, PrintWriter, and StringWriter classes and interfaces.