2010-11-16 2 views
2

Je n'arrive pas à comprendre comment transtyper correctement un objet générique dans Java en un type qui étend l'objet générique.Java Generics: type problème d'incantation (ClassCastException)

Par exemple, je dis une configuration comme ceci:

public class Parameters extends SomeCustomMap<String, String> 
{ 
    ... 
} 

public class SomeCustomMap<K, V> implements Map<K, V> 
{ 
    public SomeCustomMap<K, V> getSubSet(...) 
    { 
     SomeCustomMap<K, V> subset; 

     ... 

     return subset; 
    } 
} 

class ExampleApp 
{ 
    private void someMethod() 
    { 
     Parameters params; 
     Parameters paramsSubSet; 

     try 
     { 
      ... 

      paramsSubSet = (Parameters) params.getSubSet(...); 
     } 
     catch(Exception e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

Exécution de code similaire à ce qui précède jette toujours un ClassCastException, les gens dont je ne comprends pas bien. Toute assistance pour la mise en place d'un scénario similaire à celui ci-dessus serait appréciée! À savoir, comment est-ce que je pourrais renvoyer correctement l'objet SomeCustomMap renvoyé par la méthode params.getSubSet (...) à un objet Parameters?

Merci d'avance!

+0

Pourriez-vous montrer le code plus pour 'getSubSet' (en particulier les parties qui construisent l'objet qui sera renvoyé par' sous-ensemble de retour; '), ainsi que des préséances dans la classe' Parameters' qui changent l'objet qui sera retourné par 'getSubSet')? –

Répondre

1

Vous pouvez essayer quelque chose comme ceci:

public <T extends SomeCustomMap<K, V>> T getSubSet(...){ 
    T subset = (T)this.clone(); 
    subset.clear(); 

    return subset; 
} 

création ressemble un peu drôle - se sentir libre de changer à tout ce que vous voulez :)

En bonus, vous aurez pas besoin de lancer :)

paramsSubSet = params.getSubSet(...) 
+0

Merci! J'ai effectivement essayé quelque chose de très similaire à cela, mais je n'ai fait que transtyper sur T au retour, plutôt que de créer une nouvelle instance de T (comme les autres commentateurs l'ont souligné) ... Tout est logique maintenant. Merci tout le monde! – flux

1

Bien que j'ai commenté demander plus d'informations, basé sur ce que vous avez posté jusqu'à présent, je pense que getSubSet construit un SomeCustomMap pour retourner (avec new SomeCustomMap) quelque part. Si vous ne surchargez pas getSubSet dans Parameters, alors Parameters.getSubset renverra un SomeCustomMap (la classe de base), pas un Parameters, donc votre image vers Parameters échoue.

(pointe chaude, si vous remplacez getSubSet dans la classe Parameters, vous pouvez changer le type de retour à Parameters et éviter le transtypage.)

+0

Oui, vous avez raison de faire un 'someSubset = new SomeCustomMap ()' et de remplir ce sous-ensemble avec les données du parent ('this'). Et pour l'instant, disons que je n'ai pas de remplacements dans la classe Parameters; Est-il possible de le faire sans le remplacement? – flux

+0

Je ne sais pas quelle est la meilleure façon. Je vois que vous avez déjà accepté une réponse qui utilise 'clone()', mais vous pouvez aussi utiliser des méthodes sur 'this.getClass()', ou vous pouvez factoriser une (petite) fonction protégée dont le seul but est de construire un objet de le bon type, et remplacer cela. –

2

Votre problème est que le Sous-ensemble retourné par getSubSet est l'instance SomeCustomMap et pas de paramètres.

Ce problème ne concerne pas les génériques. Vous aurez le même problème si vous n'avez pas utilisé de génériques. Je ne sais pas comment vous créez une instance de sous-ensemble, mais vous pourriez peut-être utiliser le modèle de modèle de modèle et certains génériques pour résoudre votre problème.

1

Les génériques n'ont rien à voir avec le cast (sauf qu'en raison de la nature de l'effacement, les paramètres génériques ne peuvent pas être vérifiés pendant un cast).

Si vous obtenez un ClassCastException dans ce cas, cela signifie que l'objet renvoyé n'est vraiment pas une instance de Parameters. Juste avant de lancer, essayez d'appeler

System.out.println(params.getSubSet(...).getClass()); 

et de voir la classe d'exécution réelle du sous-ensemble. Les chances sont que le problème réside ailleurs, car votre attente que le sous-ensemble est un objet Parameters est presque certainement pas correct au moment de l'exécution - c'est un SomeCustomMap ou une autre autre sous-classe de celui-ci.

0

Comme d'autres l'ont expliqué, le problème est que l'objet réel que vous construisez dans getSubSet() n'est pas une instance de Parameters.

Voici une solution de contournement possible. Je ne l'aime pas, mais c'est un moyen de déclarer la méthode dans SomeCustomMap mais sa valeur de retour doit être tapée correctement pour n'importe quelle sous-classe.

public static <T extends SomeCustomMap<K, V>> getSubSet(T fullSet) 
{ 
    T subset; 

    ... (use fullSet instead of this) 

    return subset; 
}