2016-03-18 8 views
4

Manchmal in Java zu implementieren, gibt es einen Fall, in dem Sie eine Bibliothek verwenden, die eine final class Car und möchten es implementiert einige Vehicle Schnittstelle liefert, so dass Sie Truck machen könnte und Bus Klassen und behandle sie alle als Fahrzeuge in deinem Code. Aber Auto ist endgültig und es implementiert keine Schnittstellen.eine endgültige Klasse auf eine kompatible Schnittstelle, die die Klasse erhebt keinen Anspruch auf

Wie kann ich die letzte Car Klasse von jemand anderem in meine Vehicle Schnittstelle einwerfen, damit ich sie wie meine anderen Fahrzeuge weitergeben kann? Jede Instanzmethode in Vehicle wäre zu 100% kompatibel mit einer ähnlichen Methode in Bezug auf Instanzmethodennamen, Argumenttypen und Rückgabetypen. Es wäre äquivalent aus einer Duck-Typing-Perspektive.

Ich weiß, ich könnte eine Wrapper-Klasse machen, die nur jeden Methodenaufruf an ein internes Auto-Objekt delegiert. Das wäre der Java-Weg. Aber ich frage mich nur, ob es eine Technik gibt, um tatsächlich eine Klasse auf eine nicht verwandte (aber 100% kompatible) Schnittstelle zu übertragen. Es ist in Ordnung, wenn die Antwort böse ist.

+5

Nein, die Wrapperlösung (Zusammensetzung) ist der einzige Weg. –

+0

Sie könnten eine JDK-Proxy-Schnittstelle verwenden. – chrylis

+0

@PaulBoddington Ist das eine JVM-Einschränkung oder eine Java-Sprachbeschränkung? Ich meine, konnte ich mit dem Klassenlader oder dem Bytecode diese Einschränkung umgehen? Ich denke Clojure und Groovy können so etwas machen. – GlenPeterson

Antwort

-1

Ich würde persönlich, wie Sie erwähnten, eine Wrapper-Klasse dafür erstellen.
Vielleicht könnte ein Verweis auf die Car Schnittstelle helfen?

public abstract class MyCar implements Vehicle { 

    private Car car; 

    (...) 
} 

Es gibt nicht sehr viel Möglichkeiten, dies zu tun, und ich bin nicht sicher, dass meine Art und Weise ist, was Sie wollen, noch wenn es voll gültig.

+1

Sorry, das ist wirklich keine "Antwort" wert. Wenn überhaupt, sollten Sie eine Erklärung liefern, warum dies der einzige Weg ist, dies zu tun. – GhostCat

+1

Außerdem benötigen Sie 'implementiert Fahrzeug' - es ist eine Schnittstelle. –

5

Tun Sie es mit einem Proxy:

import java.lang.reflect.Proxy; 

public class DuckTyping { 

    static final class Car{ 
     public void honk(){ 
      System.out.println("honk"); 
     } 
    } 

    interface Vehicle{ 
     void honk(); 
    } 

    public static void main(String[] a){ 
     Car c = new Car(); 
     Vehicle v = (Vehicle) Proxy.newProxyInstance(Vehicle.class.getClassLoader(), new Class[]{Vehicle.class}, (proxy, method, args) -> 
      Car.class.getMethod(method.getName(), method.getParameterTypes()).invoke(c, args) 
     ); 

     v.honk(); 
    } 
} 

Allgemeine Methode:

static <T> T proxyCast(Object targetObject, Class<T> targetClass) { 
     return (T) Proxy.newProxyInstance(targetClass.getClassLoader(), new Class[]{targetClass}, (proxy, method, args) -> 
      targetObject.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(targetObject, args) 
     ); 
    } 
2

Der einfachste Weg, dies mit JDK Proxies zu tun:

public class DuckTyper { 

    public static <T> T quack(Object target, Class<T> duck) { 

     return (T) Proxy.newProxyInstance(duck.getClassLoader(), new Class[] { duck }, new DuckCostume(target)); 

    } 

    private static class DuckCostume implements InvocationHandler { 

     private final Object target; 

     public DuckCostume(Object target) { 

      this.target = target; 

     } 

     @Override 
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 

      Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes()); 
      return targetMethod.invoke(target, args); 
     } 

    } 

} 

(Es gibt einen Strauß von unbehandelten Ausnahmen und keiner der Reflektionen wird zwischengespeichert, also wird es langsam sein, bis du das tust, aber das sollte dir einen Startpunkt geben)

Danach können Sie Ente geben Sie Ihr Auto wie folgt aus:

Vehicle carVehicle = DuckTyper.quack(car, Vehicle.class); 
3
  1. A letzte Modifikator in einer Klasse wurde gemacht, um keine Änderungen in ihrem Zustand zu erlauben, so dass es gibt es keine Möglichkeit, zu ändern die Klasse.

  2. Guss würde nur für Sie arbeiten, wenn Sie von einer übergeordneten Klasse erweitern oder an eine Schnittstelle implementieren, gibt es keine andere Art und Weise (aber Sie können nicht, weil die Klasse ist endgültig)

  3. In diesem Fall Sie haben drei Optionen: 1. Wrapper (Sie erwähnen). 2. Reflexion (Sie können nützliche Informationen über die Klasse und ihre Methoden zur Laufzeit herausfinden). 3. Erstellen Sie Ihre eigene Klasse.

Verwandte Themen