2010-12-27 2 views
3

J'essaie de créer une classe qui implémente une interface de la bibliothèque Java. ResultSet pour être spécifique, bien que l'interface particulière ne devrait pas être pertinente à la question. (J'ai besoin de mettre une couche au-dessus d'un ResultSet régulier qui fournit des fonctionnalités supplémentaires, mais je voudrais que les fonctions "régulières" passent, et j'ai quelques fonctions qui devraient être capables de prendre soit un ResultSet régulier ou mon "ResultSet" "amélioré".)implémentation de l'interface sur plusieurs versions

Mon problème est le suivant: y a-t-il un moyen de faire cela pour que la classe puisse compiler avec succès dans Java 5 et Java 6?

Plusieurs fonctions sont déclarées dans l'interface ResultSet dans Java 6 qui renvoient des objets non définis dans Java 5. Si j'inclue ces fonctions, ma classe ne compilera pas dans Java 5 car je référence des types non définis. Mais si je n'inclue pas ces fonctions, alors ma classe ne compilera pas en Java 6 car je n'implémente pas complètement l'interface. Je semble être coincé dans quelque chose d'un catch-22. Je n'ai besoin d'aucune de ces fonctions - en fait, ma mise en œuvre ne fait que lancer une exception «non implémentée» pour chacune d'entre elles. Certains de nos programmeurs exécutent Java 5 et d'autres exécutent Java 6. Notre environnement de production est Java 5. Je suppose que dans un monde plus parfait, nous utiliserions tous la même version. Mais même si je peux modifier notre environnement pour rendre le problème théorique dans ce cas, ce problème se pose sûrement avec des projets open source. Et si je modifie mon code pour qu'il fonctionne avec Java 5, alors quand, tôt ou tard, nous passerons à Java 6, la classe se cassera, ce qui semble assez ennuyeux.

Mise à jour

Eh bien, merci pour les réponses. J'espérais plutôt que quelqu'un me dise: "Oh, si vous ajoutez juste cette annotation ou tapez la lettre" W "ici, tout fonctionnera comme par magie." Pas de chance, je suppose. Toutes les réponses reçues (à partir de cette mise à jour, de toute façon) font de bons points, donc je les ai tous mis à jour et j'ai donné le prix "meilleure réponse" à celui qui exprime le mieux ma frustration. :-)

Je pense que ma vraie solution va être d'abandonner l'idée d'implémenter ResultSet et de créer une nouvelle interface qui inclut seulement les fonctions de ResultSet qui doivent être implémentées pour que la bête fonctionne. Je trouve cela émotionnellement insatisfaisant mais, semble-t-il, meilleur que les alternatives.

Répondre

2

Je pense que la suggestion Proxy est la meilleure réponse, mais permettez-moi d'ajouter deux considérations:

d'abord, de changer une API publique en ajoutant des méthodes à une interface, les méthodes qui dépendent de classes qui n'existaient pas dans versions antérieures, est en général une abomination (un développeur qui implémente cette interface, comme vous-même, est inévitablement conduire à des problèmes). Cette abomination est rare, et est atténuée par la deuxième considération:

L'API JDBC est, bien sûr, une API publique. Mais pas très public. Je veux dire que normalement ses interfaces doivent être implémentées uniquement par les fournisseurs de pilotes JDBC. Et dans ce cas, il est tolérable d'avoir différentes implémentations pour différentes versions de JDBC - see for eg. Ce que je vise est de vous décourager d'implémenter une classe qui implémente/enveloppe un ResultSet, sauf pour des situations très spécifiques (de bas niveau). En général, il est mauvais de passer un ResultSet aux couches bussiness; Dans le cas typique, le pilote JDBC instancie le ResultSet, la méthode DAO l'utilise, mais ne le renvoie jamais.

2

Je pense que ce que vous devez faire est d'implémenter le service en tant que proxy JDK en utilisant la classe Proxy.

Il a été autour depuis 1.3, donc il fonctionnera dans toutes les versions.

La syntaxe est:

ResultSet rs = Proxy.newProxyInstance(
        ResultSet.class.getClassloader(), 
        new Class[]{ResultSet.class}, 
        new ResultSetInvocationHandler() 
); 

Et ResultSetInvocationHandler serait une mise en œuvre de InvocationHandler qui intercepte certaines méthodes avec une logique personnalisée et passe par d'autres. Il s'agit d'un ancien tutorial about the proxy mechanism.

+0

Cela détruira la sécurité de type statique et impliquera l'écriture d'une masse de plaquette. Ce serait sujet aux erreurs et inefficace. Mais ça marcherait certainement! –

+0

@ Tom Anderson * Cela détruira la sécurité de type statique * Je sais et c'est ce que je n'aime pas non plus. Mais tout ce qui a de la sécurité dans le temps de compilation nécessiterait l'utilisation de CGLIB et al –

2

Ah oui, la grande débâcle de compatibilité descendante de JDBC. Vous avez le droit de libérer deux pintes de jure solide en direction de Sun pour celui-ci.

Je ne pense pas que vous ayez un moyen d'utiliser exactement les mêmes classes d'implémentation pour 5 et 6.Pourriez-vous, cependant, faire quelque chose comme:

abstract class JaysBaseSuperResultSet /* does not implement ResultSet */ { 
    // all needed methods for java 5 
} 

class JaysSuperResultSetForJava6 extends JaysBaseSuperResultSet implements ResultSet { 
    // all additional java 6 methods 
} 

class JaysSuperResultSetForJava5 extends JaysBaseSuperResultSetForJava5 implements ResultSet { 
    // class body can be empty; compile this only with java 5 
} 

Vous aurez besoin d'avoir un processus de construction un peu bizarre: je pense que la chose la plus simple est de compiler tous, mais JaysSuperResultSetForJava5 avec le compilateur 6, mais avec -target 5, puis de compiler cette classe seule avec un compilateur 5, ou avec le compilateur 6 mais câblé aux 5 bibliothèques (ce qui peut être fait et pourrait être plus simple).

Vous aurez alors besoin d'une classe de fabrique qui détecte la version actuelle et crée une instance de la classe concrète appropriée.

Vous pouvez ensuite tout emballer dans un fichier JAR unique.

Cela semble-t-il fonctionner?

EDIT: réalisé que vous pouvez le faire avec trois classes, pas quatre.

1

Plutôt que de créer une classe qui implémente ResultSet, placez un ResultSet dans une nouvelle classe.

Une mise en garde: Pensez à utiliser les médicaments génériques pour faire revenir le sous-interface correcte de ResultSet:

public class MyWrapper<T extends ResultSet> { 
    T myResultSet; 

    public MyWrapper(T myResultSet) { 
     this.myResultSet = myResultSet; 
    } 

    public T getResultSet() { 
     return myResultSet; 
    } 

    public void setResultSet(T myResultSet) { 
     this.myResultSet = myResultSet; 
    } 

} 

(Remarque, je ne l'ai pas testé).

Note:
Création d'une nouvelle classe qui implémenteResultSet peut sembler une bonne idée sur la surface, mais dégénère rapidement en folie en raison de ResultSet ayant déjà sa propre subinterface RowSet, qui à son tour a sa propre subinterface JdbcRowSet , qui à son tour a ses propres sous-interfaces ... et à la toute fin de cet arbre sont les objets réels qui font partie du pilote JDBC pour votre base de données.

+0

. Le problème ici est que nous avons la bibliothèque A qui inclut une fonction qui prend un ResultSet en tant que paramètre. Je travaille sur le projet B, qui utilise la bibliothèque A. Je pourrais, en principe, surcharger la fonction dans la bibliothèque A pour prendre une instance de ma nouvelle classe dans le projet B.Mais alors je construis une dépendance sur B en A, ce qui me semble être une mauvaise idée. Si tout le reste échoue, peut-être je vais définir une interface dans la bibliothèque A qui est un petit sous-ensemble de ResultSet, juste les fonctions que nous utilisons réellement. Mais alors je devrais dupliquer tout le code dans la version qui prend ResultSet dans une autre fonction en prenant un type différent. – Jay

+0

Je ne me soucie pas vraiment de ce qui est "en dessous" d'un ResultSet. Je n'écris pas ma propre interface JDBC. J'écris juste une classe qui fournit des fonctions supplémentaires dont nous avons besoin, une gestion des erreurs, etc., tout en voulant donner à l'appelant l'accès aux méthodes de base suivantes et getter. Donc, je ne suis pas en ajoutant une couche ci-dessous, j'ajoute une couche ci-dessus. Tout ce que je ne devrais pas savoir ou prendre en compte. – Jay

+0

@jay qui sonne vraiment comme si vous utilisiez Spring JDBC. Je pense que tu ré-invente la roue. –

Questions connexes