How to 'wrap' two classes with identical methods?

前端 未结 6 946
误落风尘
误落风尘 2021-02-15 10:52

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 t

相关标签:
6条回答
  • 2021-02-15 11:31

    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.

    0 讨论(0)
  • 2021-02-15 11:32

    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
    
    0 讨论(0)
  • 2021-02-15 11:33

    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); }
      }
    }
    
    0 讨论(0)
  • 2021-02-15 11:40

    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.

    0 讨论(0)
  • 2021-02-15 11:47

    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());
    
    0 讨论(0)
  • 2021-02-15 11:54

    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);
     }
     ...
    
    }
    
    0 讨论(0)
提交回复
热议问题