Java - Remplacement du type de retour de l'interface étendue lorsque le type de retour utilise des génériques pour ses propres types de paramètres de méthode
j'ai trébuché sur une curiosité dans le java héritage, et je voulais vous demander de meilleures idées sur:
Assumer deux interfaces A et A1
Interface A1 s'étend Un
Interface Un a une méthode qui renvoie un type générique.
Le type générique serait comme GenericType<T>
.
Une idée de base est maintenant de changer ce générique type de retour de
GenericType<Object>
dans Une Interface en
GenericType<String>
dans l'Interface A1
Bien semble facile au premier abord (mauvaises choses viendra plus tard)
Nous déclarer Une Interface comme
public interface InterfaceA {
public GenericType<? extends Object> getAGenericType();
}
et Interface A1 comme
public interface InterfaceA1 extends InterfaceA
{
@Override
public GenericType<String> getAGenericType();
}
Comme vous le voyez, nous sommes obligés d'écrire des GenericType<? extends Object>
dans l'Interface elle-même pour permettre la remplaçant avec des génériques de base "sous-classes".
(En fait, le paramètre générique de la generictype est sous-classé pas le type générique lui-même)
Désormais assumer la GenericType a sa propre méthode de recherche comme:
public interface GenericType<D>
{
public void doSomethingWith( D something );
}
Maintenant essayer d'instancier A1 fonctionne très bien.
Plutôt d'essayer d'instancier Un va sucer. Pour voir pourquoi chercher à partir de ce "utilisation de l'interface" de la classe:
public class LookAtTheInstance
{
@SuppressWarnings("null")
public static void method()
{
InterfaceA a = null;
InterfaceA1 a1 = null;
GenericType<String> aGenericType = a1.getAGenericType();
GenericType<? extends Object> aGenericType2 = a.getAGenericType();
Object something = null;
aGenericType2.doSomethingWith( something );
}
}
Vous demander: "Et maintenant?"
Il ne fonctionne pas sur les dernières lignes. En fait, le paramètre "quelque chose" n'est même pas de type "Objet", il est de Type "? s'étend de l'Objet". Si vous ne pouvez pas transmettre la déclaration de type "Objet". Vous ne pouvez pas passer quoi que ce soit.
Donc, vous vous retrouvez en déclarant nice interfaces qui, comme il s'avère, ne peut pas être instanciée droit.
Avez-vous des idées sur la façon de modéliser un tel cas d'utilisation, où les sous-classes devront remplacer le type de retour, tandis que le type de retour est un des génériques?
Ou comment feriez-vous autour d'un tel modèle?
Ou suis-je manque juste un simple point dans le générique de déclaration et mon exemple est possible de cette façon?
----------- (1) modifier grâce aux réponses -----------
Une très bonne idée de base est de rendre l'interface la plus abstraite! J'ai eu exactement la même idée première, mais... (ce qui a à venir)
Assumer ce faire:
Nous introduisons une nouvelle interface AGeneric
public interface InterfaceAGeneric<T>{
public GenericType<T> getAGenericType();
}
Maintenant, nous allons avoir à étendre A et A1 à partir de cette nouvelle interface:
public interface InterfaceA extends InterfaceAGeneric<Object>{}
public interface InterfaceA1 extends InterfaceAGeneric<String>{}
Qui fonctionne très bien, malgré qu'elle rompt le chemin de l'héritage originel.
Si nous voulons A1 toujours être extensible à partir d'Un, nous devons changer de A1 à
public interface InterfaceA1 extends InterfaceA, InterfaceAGeneric<String>{}
et il y a un problème, c'est nouveau. Cela ne fonctionne pas, puisque nous étendre indirectement la même interface avec les différents types génériques. Ce n'est malheureusement pas autorisé.
Vous voyez le problème?
-
Et à point à un autre circonstance:
Si vous lancez la GenericType<? extends Object>
à GenericType<Object>
il fonctionne évidemment.
Exemple:
public class LookAtTheInstance
{
public static void main( String[] args )
{
InterfaceA a = new InterfaceA()
{
@Override
public GenericType<? extends Object> getAGenericType()
{
return new GenericType<Object>()
{
@Override
public void doSomethingWith( Object something )
{
System.out.println( something );
}
};
}
};
;
@SuppressWarnings("unchecked")
GenericType<Object> aGenericType2 = (GenericType<Object>) a.getAGenericType();
Object something = "test";
aGenericType2.doSomethingWith( something );
}
}
Donc, il me semble que la résolution du type de paramètre de la méthode
public interface GenericType<D extends Object>
{
public void doSomethingWith( D something );
}
est faux.
Si D est unifié avec "? s'étend de l'Objet" pourquoi le type de paramètre n'est pas forcé d'être "Objet"?
Pas plus de sens?
source d'informationauteur Omnaest | 2011-02-20
Vous devez vous connecter pour publier un commentaire.
Ce n'est pas possible, parce que Java Génériques sont invariantes. [1]
Que vous l'avez trouvé, vous ne pouvez pas avoir une interface de déclarer une méthode qui retourne
GenericType<Object>
et dans une sous-interface de remplacer la méthode pour retournerGenericType<String>
: ce dernier type de retour n'est pas un sous-type de l'ancien. Et pour une bonne raison!Vous avez essayé de
Il n'est en aucune façon pourrait éventuellement fonctionner: E. g. quel doit être le type de
E
danspublic E set(int index, E element)
dans une classe que mis en œuvre à la foisList<String>
etList<Object>
? Votre sous-classé interface produisent une semblable hybride: La valeur de retour degetAGenericType
dans la sous-interface pour la mise en œuvre à la fois laGenericType<String>
et laGenericType<Object>
interface. Et comme nous l'avons vu, c'est impossible.Le compilateur ne sait pas ce que vous allez faire avec le paramètre de type dans
GenericType
(bien qu'il pourrait théoriquement le savoir, il n'est pas). Si vous avez eu une variable de typeGenericType<String>
et affectée d'unGenericType<Object>
vous pouvez très bien mettre unLong
cas où unString
est prévu, et obtenir uneClassCastException
où vous ne vous attendez pas un.Dans le
doSomethingWith
méthode de votre variableGenericType<? extends Object> aGenericType2
vous pouvez passer d'une chose:null
.null
est la seule référence de l'objet qui a un sous-type de? extends Object
. La limite inférieure type de? extends Object
est le type nullqui ne peut être exprimée en Java, et que de manière implicite existe comme le type de lanull
de référence.[1] http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29#Java
@Override annotation:
Avec cette annotation vous ne pouvez pas changer le type de retour de la fonction.
Si vous voulez remplacer le type de retour, juste faire de l'interface d'Une manière plus abstraite, ajouter un générique de cette interface:
L'échantillon sur la substitution d'un générique de la méthode dans une classe générique.
Je ne sais pas si c'est ce que vous attendez, mais Vous pouvez déclarer votre interface de quelque chose comme cela
Alors que votre classe pourrait ressembler
public class InterfaceImpl extends Interface<String> { ... }
Le problème, c'est que
InterfaceA
ne sais pas quel type c'est la tenue. Si vous obtenez InterfaceA de prendre un argument générique alors que vous pourriez faire ceci:Je suis plusieurs années de retard à la fête, mais j'ai trouvé cette page en cherchant une question connexe, et aucune réponse n'a vraiment frappé sur la question centrale, qui je pense, mérite d'être précisée. Regardons un peu plus étoffé, exemple:
Sur le plan conceptuel, GenericType n'est PAS un GenericType, depuis un GenericType ne peut doSomethingWith() Cordes, tandis qu'un GenericType doit être en mesure de doSomethingWith() n'importe quel objet. GenericType est un compromis qui le compilateur permet de l'utiliser comme une "classe de base" pour tout GenericType où D est un Objet, mais seulement vous permet d'utiliser une référence de ce type, pour l'appel de méthodes qui sont de type sécurisé pour toute valeur d'exécution de '?' (comme getAValue(), dont la valeur de retour peut toujours être en toute sécurité cast d'un Objet depuis D-un Objet indépendamment de runtime type).
Il est difficile de dire ce que (le cas échéant) de l'affiche originale a été effectivement essayer de modéliser avec ce code, et en particulier la façon dont beaucoup de générique-ness de GenericType était vraiment nécessaire, mais peut-être l'héritage aurait du aller dans l'autre sens?
L'inconvénient étant qu'il n'y a pas de bonne façon d'exprimer le compilateur java qui NonGenericType s'étend GenericType, même si en théorie, elle pourrait dans ce cas, puisque GenericType n'utilise jamais D une valeur de retour. Vous devez spécifier manuellement chaque GenericType que vous souhaitez étendre. 🙁
Je pense que le but de
InterfaceA
est de ne pas être instancié à tous, parce que l'un de ses fiable classes génériques. C'est ce que vous vouliez dire en déclarant: