2016-03-18 1 views
4

Parfois, en Java, il y a un cas où vous utilisez une bibliothèque qui fournit un final class Car et vous souhaitez mettre en place une interface Vehicle pour que vous puissiez faire Truck et Bus classes et les traiter tous comme des véhicules dans votre code. Mais Car est définitif et n'implémente aucune interface.Convertir une classe finale en une interface compatible que la classe ne prétend pas implémenter

Comment puis-je transférer la classe Car finale de quelqu'un d'autre à mon interface Vehicle pour que je puisse la transmettre comme mes autres véhicules? Chaque méthode d'instance sur Vehicle serait compatible à 100% avec une méthode similaire sur Car en termes de noms de méthode d'instance, de types d'argument et de type de retour. Ce serait l'équivalent d'une perspective Duck-Typing.

Je sais que je pourrais créer une classe wrapper MyCar extends Vehicle qui ne ferait que déléguer chaque appel de méthode à un objet Car interne. Ce serait le Java Way. Mais je me demande simplement s'il existe une technique pour lancer une classe sur une interface non liée (mais 100% compatible). C'est OK si la réponse est mauvaise.

+5

Non, la solution d'emballage (composition) est la seule voie. –

+0

Vous pouvez utiliser une interface proxy JDK. – chrylis

+0

@PaulBoddington Est-ce une limitation JVM ou une limitation de langage Java? Je veux dire, avec le piratage du classloader ou bytecode, pourrait-on contourner cette limitation? Je pense que Clojure et Groovy peuvent faire quelque chose comme ça. – GlenPeterson

Répondre

-1

Je voudrais personnellement, comme vous l'avez mentionné, créer une classe d'emballage pour cela.
Peut-être qu'une référence à l'interface Car pourrait vous aider?

public abstract class MyCar implements Vehicle { 

    private Car car; 

    (...) 
} 

Il n'y a pas des moyens beaucoup à faire, et je ne suis pas sûr que mon chemin est ce que vous voulez, ni si elle est tout à fait valable.

+1

Désolé, cela ne vaut vraiment pas une "réponse". Si du moins vous devriez fournir une explication pourquoi c'est la seule façon de le faire. – GhostCat

+1

En outre, vous auriez besoin de 'implements Vehicle' - c'est une interface. –

5

le faire avec un 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(); 
    } 
} 

méthode générique:

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

La meilleure façon de le faire est avec JDK Procurations:

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); 
     } 

    } 

} 

(Il y a un tas des exceptions non gérées et aucune de la réflexion n'est mise en cache, donc ce sera lent jusqu'à ce que vous le fassiez, mais cela devrait vous donner un point de départ)

Après cela, vous pouvez canard taper votre voiture comme ceci:

Vehicle carVehicle = DuckTyper.quack(car, Vehicle.class); 
3
  1. Un modificateur finale dans une classe a été fait pour ne pas permettre à des changements dans son état il est donc il n'y a aucun moyen pour modifier la classe.

  2. Cast ne ne fonctionne pour vous si vous étendez d'une classe mère ou mettre en œuvre à une interface, il n'y a pas d'autre moyen (mais vous ne pouvez pas parce que la classe est définitive)

  3. Dans ce cas, vous avez trois options: 1. Wrapper (vous mentionnez). 2. Réflexion (vous pouvez trouver des informations utiles sur la classe et ses méthodes en cours d'exécution). 3. Créez votre propre classe.