Comment faire pour forcer un paramètre de type générique pour être une interface?
Est-il un moyen en java à préciser, que le paramètre de type générique d'une classe doit être une interface (pas seulement en l'étendant!)
Ce que je veux faire est la suivante:
public class MyClass<X extends SomeInterface, Y extends SomeOtherClass & X>
Sens que Y doit être une sous-classe de SomeOtherClass ET de mettre en œuvre X.
Ce que j'ai actuellement par le compilateur est
Le type de X n'est pas une interface; il ne peut pas être spécifié comme un délimitée paramètre
Alors, comment puis-je dire au compilateur que X doit toujours être une interface?
Edit:
OK, je suppose que j'ai simplifié mon problème un peu. Nous allons utiliser mon vrai domaine d'application pour la rendre plus claire:
J'ai une API pour la représentation de diagrammes. Un Diagramme contient Nœud et Bord objets. L'ensemble de ces trois classes implémentent l' Forme interface. Les formes peuvent demander à l'enfant de formes, un parent de la forme et appartiennent à un diagramme.
Le truc, c'est que j'ai besoin de faire deux versions de cette API: un open-source avec juste des fonctionnalités de base et une grande étendue avec plus de fonctionnalités. Toutefois, l'API étendue doit seulement fournir des méthodes qui renvoient l'étendue des types (ExtendedDiagram, ExtendedNode, ExtendedEdge et (ici vient le problème) ExtendedShape).
J'ai donc quelque chose comme ceci:
/* BASIC CLASSES */
public interface Shape<X extends Shape<X,Y>, Y extends Diagram<X,Y>>{
public List<X> getChildShapes();
public X getParent();
public Y getDiagram();
...
}
public class Diagram<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...}
public class Edge<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...}
...
/* EXTENDED CLASSES */
public interface ExtendedShape extends Shape<ExtendedShape,ExtendedDiagram> { ... }
public class ExtendedDiagram extends Diagram<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... }
public class ExtendedEdge extends Edge<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... }
...
L'API étendue fonctionne bien et la base de code d'API donne des avertissements, mais le principal problème se produit lors de l'utilisation de l'API:
public class SomeImporter<X extends Shape<X,Y>, Y extends Diagram<X,Y>, E extends Edge<X,Y>>{
private Y diagram;
public void addNewEdge(E newEdge){
diagram.addChildShape(newEdge);
...
Que la dernière ligne me donne l'avertissement suivant:
La méthode addChildShape(X) dans le type de Schéma n'est pas applicable pour les arguments (E)
Donc maintenant, je voudrais juste préciser que E doit également mettre en œuvre des X et que tout irait bien - je l'espère 😉
N'tous les sens? Savez-vous un moyen de le faire? Ou est-il même une meilleure façon d'obtenir l'API étendue avec lesdites restrictions?
Merci pour coller avec moi, toute aide est grandement appréciée!
- stackoverflow.com/questions/132353/...
- Cette erreur est un peu incorrect. Cela signifie vraiment que vous avez besoin de se référer à une véritable classe ou de l'interface et PAS un autre type générique (c'est à dire
Y extends SomeOtherClass & SomeInterface
, comme indiqué dans le lien fourni par @Yochai Timmer) - Mais que la chose: j'ai besoin d'un générique, parce que j'ai déjà un subinterface de SomeInterface (appelons cela ExtendedInterface). Maclasse est parfois utilisé avec SomeInterface et parfois avec ExtendedInterface. En outre, il peut très bien y avoir d'autres extensions de SomeIterface dans l'avenir... Peut-être, j'ai simplifié mon problème énoncé un peu trop, je vais modifier ma question initiale.
- Pouvez-vous ajouter de la signature de la méthode pour votre addNewEdge? - note de côté: c'est Qu'une très compliquées ensembles de Génériques que vous servez, vous n'avez pas avoir le vertige?
- Vous pouvez voir la signature de la méthode pour addNewEdge dans mon édité postes. Si vous voulez dire addChildShape puis que serait la suivante: public void addChildShape(X newShape); (dans la Forme de l'interface définie ci-dessus)
- Et oui, il est assez complexe. Surtout en considérant le fait que je ne veux obtenir le droit de retour 😉
Vous devez vous connecter pour publier un commentaire.
Vous pouvez utiliser:
Une classe Foo avec un paramètre de type, T. Foo doit être instancié avec un type qui est un sous-type de Numéro et qui implémente Comparable.
Dans les génériques contexte,
<Type extends IInterface>
gère à la fois prolonge et met en œuvre. Voici un exemple:GenericsTest acceptera GT car il implémente Runnable. GT2 n'est pas, par conséquent, il échoue lorsque vous essayez de compiler ce deuxième GenericsTest de l'instanciation.
Peut-être vous pouvez simplifier votre modèle un peu: trop génériques rapidement devenir un véritable casse-tête en termes de lisibilité, et c'est tout à fait un problème si vous définissez une API publique. Habituellement, si vous ne pouvez pas comprendre plus de ce qui devrait être à l'intérieur des crochets, puis vous allez trop loin pour que votre besoin, et vous ne pouvez pas attendre que les utilisateurs comprennent mieux que vous-même...
De toute façon, afin de rendre votre code de la compilation, vous pouvez essayer de définir quelque chose comme cela, dans la
Shape
type:Cela devrait le faire.
HTH
List<Shape>
peut ainsi contenirExtendedShape
s tant que clients de les voir comme de simplesShape
s... en Outre, les clients n'ayant pas accès à des fonctionnalités étendues ne sont sûrement pas en mesure de construireExtendedShape
s pour les ajouter à cette Liste.Vous écrit ce qui suit:
Je vous conseille, au minimum, de se débarrasser de la X type de variable, comme suit:
La raison en est que ce que vous a écrit à l'origine en souffre potentiellement illimitée récursive imbrication des paramètres de type. Une forme peut être imbriquées à l'intérieur d'un parent de la forme, qui peut être imbriquée dans une autre, tout ce qui doit être représenté dans le type de signature... pas une bonne recette pour des raisons de lisibilité. Eh bien, il ne se passe pas tout à fait de cette façon dans votre exemple, dans lequel vous déclarez "Forme<X>" au lieu de "Forme<Figure<X>>" mais c'est la direction que vous allez, si vous jamais eu envie d'utiliser effectivement la Forme de sa propre...
Je ne serais probablement aussi recommande d'aller une étape plus loin et de se débarrasser de la variable Y pour des raisons similaires. Java génériques n'avez pas à faire face très bien avec ce genre de composition. Lorsque vous tentez d'appliquer les types statiques pour ce type de modélisation par les génériques, j'ai trouvé que le système de type commence à se décomposer lorsque vous commencez à étendre les choses par la suite.
C'est typique de l'Animal/Chien de problème... Animal a un getChildren(), mais le Chien, les enfants doivent aussi être des Chiens... Java ne supporte pas ce bien à cause (en partie en raison de l'absence de résumé types, comme dans les langues, comme Scala, mais je ne dis pas que vous devriez se précipiter et de l'utilisation de Scala pour ce problème) le type de variables doivent commencer à être déclarée dans toutes sortes d'endroits où ils n'ont pas vraiment leur place.
Utiliser un pré-processeur pour générer de la "réduction" de la version de votre code. À l'aide de apt et des annotations peut être une belle façon de le faire.
Je pourrais être loin de la base ici, mais ma compréhension des génériques est un peu différent.
Je demande à quelqu'un de me corriger si je me trompe.
OMI -
C'est un très déroutant de la structure que vous avez. Vous avez des sous-classes de la Forme référencé à l'infini, il ressemble.
Votre Forme d'interface est utilisé de la même manière qu'une table de hachage, mais je n'ai jamais vu une HashMap faire ce que vous essayez de faire, éventuellement, vous devez avoir soit X une classe en Forme. Sinon, vous faites HashMap
Si vous voulez toujours que X soit un "EST" UNE relation à une interface, il ne se produira pas. Ce n'est pas ce que les génériques sont pour. Les génériques sont utilisés pour appliquer des méthodes à Objets multiples, et les interfaces ne peuvent pas être des Objets. Les Interfaces de définir un contrat entre un client et une classe. Tout ce que vous pouvez faire avec de la est à dire que vous allez accepter n'importe quel Objet qui implémente Runnable, parce que tous ou certains de vos méthodes sont nécessaires pour utiliser les méthodes d'interface Runnable. Sinon, si vous ne spécifiez pas et que vous définissez comme , votre contrat entre votre classe avec le client peut produire un comportement inattendu et provoquent la mauvaise valeur de retour ou la levée d'une exception.
Par exemple:
Maintenant, si vous avez cette classe, vous pouvez vous attendre que vous POUVEZ toujours appeler le "parler ()" & " sniff()'
Toutefois, si vous avez fait cela, vous NE peut pas TOUJOURS faire appel à "faire parler ()" & " sniff()'
CONCLUSION:
Génériques de vous donner la capacité d'utiliser des méthodes sur un large éventail d'objets, pas des interfaces. Votre inscription définitive dans un générique DOIT être d'un type d'Objet.
public interface Poodle extends Dog; public class StandardPoodle implements Poodle
et à l'aide depublic class Kingdom<X extends Dog>
vous pourriez faireKingdom<Poodle> myPoodle = new Kindom<Poodle>(new StandardPoodle());
Donc, ce que je voudrais faire avec Java serait:public class Kingdom<X extends Animal, Y extends SomeClass & X>{ public Kingdom(Y object){ object.eat(); //further code using SomceClass functionality }