2009-08-12 8 views
2

La méthode suivante génère un avertissement, mais il semble sûr pour moi. Je suis sûr que le problème est avec moi:Pourquoi cette méthode générique est-elle dangereuse?

public <S extends CharSequence> S foo(S s) { 
    return (S) new StringBuilder(s); 
} 

On dirait que cela reviendra toujours l'argument s. Quelqu'un peut-il montrer un exemple qui provoquerait cette méthode à lancer une exception?

Modifier: Je ne suis pas particulièrement intéressé par la question de savoir si les génériques sont nécessaires ici. Au contraire, je cherche une démonstration de la façon dont cette méthode est dangereuse.

+0

Répondant à l'insécurité, la réponse de skaffman l'explique bien, comme le font autres: la distribution (S) (après le mot-clé return) échouera si vous transmettez une chaîne en tant que paramètre, car elle ne peut pas convertir StringBuilder en chaîne. – KLE

Répondre

1

foo ("test");

est suffisant pour que java essaie de lancer StringBuilder dans une chaîne.

Votre code est garanti o se tromper, pouvez-vous expliquer ce que vous essayez d'atteindre plase?

+0

J'ai juste essayé ça, et ça a bien fonctionné. –

+0

OK, 'foo (" test ")' ne suffisait pas tout seul, mais si j'ai essayé de l'imprimer, ou l'assigner à une chaîne, je reçois l'erreur. Quant à mon but, j'essaie de comprendre. J'essaie aussi de passer l'examen SCJP, si cela peut aider. –

0

mon Java est rouillé, mais cette méthode ne pas jeter tout

exceptions
new StringBuilder(s) 

peut jeter?

+0

Je viens de regarder ça. Si 's' est nul, cela génèrera une exception, mais ce serait aussi le cas de la méthode non-générique de skauffman. –

6

Ce n'est pas sûr car si StringBuilder est une CharSequence, ce n'est pas nécessairement de type S (en fait, ce ne sera certainement pas le cas). Vous pouvez voir cela échouer simplement en passant un String dans votre méthode. Cela va construire un StringBuilder en utilisant votre String comme un argument, puis essayez de transformer votre StringBuilder en String (ce qui échouera).

Il n'y a probablement pas besoin d'utiliser des génériques tout ici, cela fonctionne bien:

public CharSequence foo(CharSequence s) { 
    return new StringBuilder(s); 
} 
+0

Toujours, je veux comprendre ce qui ne va pas avec ceci. J'avoue que j'étudie pour l'examen SCJP. –

+0

+1 Je suis d'accord avec vous quand vous dites qu'il n'est pas nécessaire d'utiliser des génériques ici – dfa

+0

Votre méthode a un type de retour S qui est défini par le type de l'objet que vous transmettez comme argument. StringBuilder sera converti en ce type lorsque la méthode sera exécutée. Si vous passez une chaîne comme argument, alors le type S est en fait une chaîne et vous ne pouvez pas convertir StringBuilder en chaîne. – skaffman

2

Par exemple, cela compilera:

String a = foo("ss"); 

mais il échouera à l'exécution:

 
ClassCastException: java.lang.StringBuilder cannot be cast to java.lang.String 

puisque foo renvoie S, le type de votre entrée pa rameter (String dans ce cas).

Je pense que vous n'avez pas besoin d'utiliser les médicaments génériques ici (comme skaffman said in his answer):

public StringBuilder foo(CharSequence s) { 
    return new StringBuilder(s); 
} 
1

Le unsafety se trouve ici pas dans la méthode elle-même (si elle a ses problèmes, aussi), mais à l'appel site. L'utilisation de S pour le type de l'argument d'entrée ainsi que pour la valeur de retour indique au compilateur que quel que soit le type d'objet qui est passé à la fonction, le résultat a le même type (ou un type dérivé , réellement).

Ainsi, le compilateur est autorisé à supposer que, dans l'appel

foo("hello, world") 

le résultat sera une java.lang.Chaîne, alors que dans l'appel

foo(new StringBuffer("hello, world")) 

le résultat sera une StringBuffer, et ainsi de suite. Dans les deux cas, cependant, votre méthode renvoie et non ce qu'elle était supposée renvoyer, à savoir un objet du même type que l'argument d'entrée. Au lieu de cela, un StringBuilder est renvoyé.

En fait, le seul genre d'argument d'entrée de votre méthode fonctionnera avec un StringBuilder, tout le reste sera vouée à l'écraser avec un ClassCastException tôt ou tard, comme le compilateur peut (et souvent) insérer (caché) jette sur les sites d'appel. Et bien sûr, comme d'autres l'ont déjà fait remarquer, l'utilisation de génériques n'est pas vraiment nécessaire ici, de toute façon.

1

Le type de retour de votre code de méthode est TOUJOURS un StringBuilder.

C'est parce que le type déclaré de l'expression « nouvelle StringBuilder (x) » est toujours un StringBuilder, quel que soit le X.

Il est TOTALEMENT inutile d'essayer de lancer ce à quoi que ce soit. L'information "S" fait partie de l'effacement, qui n'existe qu'au moment de la compilation et est effacée au moment où le programme s'exécute, c'est-à-dire à l'exécution. (Casting est une chose d'exécution exclusivement, et lancer quelque chose à un type/classe dont l'identité a été effacée pendant un run-time, est en effet totalement inutile.)