9
votes

À propos de la création de tableau générique en Java

Il suffit de trouver une ligne de code que je ne comprends pas tout à fait. XXX PRE>

Cette ligne peut transmettre la compilation sur mon IDE AndroidStudio, bien qu'avec l'avertissement. Il semble violer la règle de base de la langue orientée objet: "L'objet Super Class peut être instancié avec une instance de classe enfant, mais pas vice versa." P>

String[] ss = new Object[1];  // won't compile


7 commentaires

Bienvenue dans le monde merveilleux de l'effacement de type.


Ouais, je sais que l'effacement fonctionne au moment de l'exécution, mais le code de mon exemple ne manque-t-il pas de temps de compilation?


Je reçois type sécurité: l'expression de la liste de types [] nécessite une conversion non cochée pour être conforme à la liste [] dans Eclipse, qui est le bon avertissement. Vous avez seulement instancié le tableau à ce stade, et il ne contient qu'un pointeur NULL. Aucun Liste s a déjà été créé.


L'avertissement est là pour une raison. Il vous dit à peu près que la sécurité du type est violée; Vous êtes autorisé à plier la règle de base si vous voulez vraiment vraiment vraiment / besoin de, mais dans de nombreux cas, vous ne devriez pas.


La règle de base est que si vous utilisez déjà un type brut (c.-à-d. Liste et non list ), votre intention consiste à contourner le type post-Java-1.5 Système En ce qui concerne les contraintes de type entre types génériques. Notez que la liste est pas identique à la liste ! Il n'y a pas de relation significative entre un type brut et un type générique, et la coulée entre les deux est quelque chose que le langage Java permet un souci de compatibilité. Vous devez éviter complètement les types bruts, sauf absolument nécessaire.


De plus, si vous êtes intéressé par les génériques Nitty-Gritty de Java, la FAQ de Angelika Langer est une lecture fondamentale: angelikalanger.com/genericsfaq/javagenericsfaq.html


@Slaks - Type Erasure n'est pas vraiment la réponse à cela. L'effacement de type est le processus par lequel le compilateur génère du byTecode pour un remontage qui ne peut pas gérer les types paramétrés à un niveau bas; Cela ne répond pas pourquoi cette affectation est autorisée au niveau de la langue. Il est effectué uniquement une fois que le code est vérifié pour la sécurité du type et n'est pas vraiment pertinent pour une question sur la langue.


4 Réponses :


2
votes

Les génériques de Java ne sont que du sucre syntaxique dans le temps de compilation. Dans le temps d'exécution, les deux listes sont list


4 commentaires

Mais au moment de la compilation, le type doit être liste [] et list [] , je pense.


Bien que vrai, le fait que les génériques soient mis en œuvre par effacement de Java 8 (et plus tôt) ne résoudent rien pour résoudre le problème de l'OP. Le compilateur Java a la responsabilité de la sécurité des types avec des génériques, et pourtant ce code compile (peu importe que la création de réseau générique en Java n'est pas sécurisée).


Tout un langage de programmation est le sucre syntaxique au moment de la compilation. Ceci est une affirmation sans signification. Cela n'explique pas pourquoi une construction est ou n'est pas autorisée.


En outre, ils ne sont clairement pas list au moment de l'exécution, car aucune information de ce type n'est jointe aux objets, et il n'a pas vraiment de sens pour confondre les types de temps de compilée avec des classes d'exécution ( Comme dans des objets chargés de .Class des fichiers contenant la mise en oeuvre compilée d'un type). Au moment de l'exécution, les listes seront s, sauf pas le "type brut", car le concept même d'un type brut n'a pas de sens à l'exécution.



0
votes

Cette ligne peut transmettre la compilation sur mon IDE AndroidStudio avec Attention. Il semble violer la règle de base de l'objet orienté objet Langue: «L'objet Super Class peut être instancié avec la classe enfant instance, mais pas vice versa. "

La ligne: xxx

Il me semble que ce qui précède ne devrait pas compiler. Reconnaissant que des tableaux en Java sont covariants, je ne pense pas qu'il ne pense pas qu'il soit raisonnable de conclure que list est une super classe de son type brut.

J'ai pu Pour trouver ceci dans les JLS, SEC 4.3:

Il y a des contextes dans la langue de programmation Java où un générique Le nom de la classe ou de l'interface est utilisé sans fournir des arguments de type. Tel Les contextes n'impliquent pas l'utilisation de types bruts (§4.8). Plutôt, ils sont contextes où les arguments de type sont inutiles pour, ou non pertinents, la signification de la classe générique ou de l'interface.

Peut-être, à des fins de l'affectation ci-dessus, le type brut est autorisé car les "arguments de type sont inutiles pour ... la signification de la classe générique ou de l'interface". Une fois que le type brut est affecté à StressLists , il doit s'agir d'une liste . certes inattendu, si vrai.


6 commentaires

Je ne trouve pas cela dans les JLS, toute la citation me semble étrange. Clairement, le contexte de l'OP implique l'utilisation d'un type brut, de sorte qu'une autre partie de la spécification s'appliquerait. Le code de l'OP compile car les expressions impliquant des types bruts sont spécialement traitées dans le compilateur et éventuellement des affectations non sûres sont autorisées. Liste n'est pas une super catégorie de liste , ni inversement, ils n'ont pas ce type de relation du tout. La relation "Type RAW" vs "type paramétré" est quelque chose de complètement non liée à la superclasse / sous-classe une.


@Millimoose: Eh bien, je vous assure que je n'ai pas fait la citation, si c'était la base de votre vote en direct. Les affectations FWIW, "et éventuellement sans sécurité sont autorisées" est une resynthèse de ma réponse (sans explication) en tout cas.


Ce n'était pas ça, mais tout ce que j'ai mentionné. Le fait que la citation ne s'applique pas, car un type brut est en fait utilisé. Que vous avez utilisé cela pour justifier pourquoi le type brut est autorisé, quand ce n'est pas parce que les paramètres de type ne sont pas pertinents. Au contraire, ils sont très pertinents, car cette affectation n'est potentiellement pas en sécurité, d'où l'avertissement. Et votre conclusion qu'il y a une relation de type entre un type brut et un type paramétré car les tableaux sont également faux.


En fait, la dernière partie devrait être l'inverse - si le type brut était un sous-type du type paramétré - que ce n'est pas - alors , et seulement alors alors Serait un tableau de covariance implique que "la matrice du type brut" est un sous-type de "tableau du type paramétré". La relation de sous-typage entre les types d'éléments de tableau est nécessaire la condition préalable à la covariance.


Rappel à cette liste .Class est illégal Bien que List.class est légal, je suppose que @millimoose est correct, "il n'y a pas de relation significative entre un type brut et un type générique." La règle OP ne s'applique donc pas à ce type d'affectation. Quoi qu'il en soit, le compilateur a le mieux essayé en donnant un avertissement. Je ne sais pas que JLS implémente les génériques, mais c'est la conclusion la plus raisonnable, je pense.


@ L14967 Pour être plus précis, il existe A relation significative entre eux, mais c'est la relation de type "Type RAW" contre "paramétré", pas un sous-typing un. Il ne nous permet pas de déterminer si l'assignation est "sûre"; Tout cela nous dit, c'est que l'affectation est "autorisée avec un avertissement", et même alors parce que c'est comment ils ont conçu Java. Tellement euh ... cela n'ajoute pas exactement plus d'informations ici. List .Class est illégal car il n'y a que la seule classe, mais il existe des moyens de représenter des types paramétrés à l'exécution: gafter.blogspot.sk/2006/12/super-type-tokens.html



2
votes

Compilation dans Eclipse I Obtenir

Cannot create a generic array of ArrayList<String>


0 commentaires

5
votes

Je pense que le JLS, Â §4.8, "Types bruts" répond à votre question actuelle de pourquoi la mission est valide:

L'utilisation des types bruts n'est autorisée que comme une concession à la compatibilité du code hérité. L'utilisation de types bruts dans le code écrit après l'introduction de génériques dans le langage de programmation Java est fortement découragé. Il est possible que les futures versions du langage de programmation Java rejette l'utilisation de types bruts.

C'est pour la compatibilité avec le code pré-générique. Généralement, si vous allez pour la sécurité de type, vous ne devez pas utiliser des tableaux de types paramétrés du tout, cela aurait été meilleur si l'auteur a écrit: xxx


< P> Votre hypothèse sur une règle de type de sécurité étant violée est erronée. Vous dites que list n'est pas un sous-type de list . Toutefois, dans le système de type Java, la réponse à la question: "est Liste un sous-type de la liste ?" n'est ni "oui", ni ce n'est "n °" C'est "cette question ne peut pas être répondue."

(Il n'est donc probablement pas correct de confondre "types" et "classes". Il n'y a qu'une seule liste classe, mais list et list sont différents types. Bonus: Liste est pas a Sous-type de Liste .)


Pour ajouter un peu plus de détails sur ce qui se passe, les JLS explique quelles conversions sont autorisées dans une variable Affectation dans §5.2, "contextes d'affectation ". Notamment, la liste se termine par:

Si, après que les conversions énumérées ci-dessus ont été appliquées, le type résultant est un type brut ( §4.8 ), une conversion non cochée ( §5.1.9 ) peut alors être appliqué.

Le dernier lien vers §5.1.9, "Conversions non cochées" , après quelques formalismes expliquant la conversion non cochée, réitère la justification (emphase de la mine):

La conversion non cochée est utilisée pour permettre une interopération en douceur du code hérité , écrit avant l'introduction de types génériques, avec des bibliothèques ayant subi une conversion pour utiliser la généricité (processus que nous appelons la production). Dans de telles circonstances (notamment les clients du cadre de collections dans java.util ), le code hérité utilise des types bruts (par exemple collection au lieu de collection < / code>). Les expressions de types bruts sont transmises comme des arguments sur des méthodes de bibliothèque utilisant des versions paramétrées de ces mêmes types que les types de paramètres formels correspondants.

Ces appels ne peuvent pas être montrés statilement en sécurité sous le système de type à l'aide de génériques. Rejeter de tels appels invaliderait les grands corps de code existant et les empêcher d'utiliser de nouvelles versions des bibliothèques. Cela à son tour, découragerait les vendeurs de la bibliothèque de tirer parti de la généricité. Pour éviter un tel tour d'événements indispensable, un type brut peut être converti en une invocation arbitraire de la déclaration de type générique auquel le type brut fait référence. tandis que la conversion est sans fond, elle est tolérée comme une concession à la pratique. Un avertissement non coché est délivré dans de tels cas.

L'histoire officielle est effectivement que des conversions non cochées de type brut au type paramétré ont été ajoutées délibérément à la langue, malgré leur potentiellement dangereuse et ne sont marquées qu'avec un avertissement de compilation pour des raisons de compatibilité. (Java essaie beaucoup de s'assurer que le code compilé dans la version X ne manque jamais de compiler ou cesse d'être utile dans la version X + 1.)


8 commentaires

Ouais, @Millimoose Je pense que vous avez raison, la règle de la sécurité de type ne s'applique que pour ce cas particulier, merci!


@ L14967 - Eh bien, il fait kiiId de s'appliquer. L'affectation est dangereuse, en ce qu'il est impossible de s'assurer que c'est sûr. (Qui nécessiterait de connaître la signature de type du côté droit.) Malheureusement, la compatibilité devait être résolue d'une manière ou d'une autre et que les concepteurs de Java ont décidé d'introduire le concept de "type brut" pour exprimer le Way Le système de type fonctionne dans le code hérité.


@ L14967 - Vous pouvez comparer et contraster à C #, qui a été entièrement partiellement: les variables et les objets ne peuvent jamais être des types spécifiques. (Même si quelque chose de similaire à «types bruts» existe dans C #, «Définitions de type générique», elles sont principalement utilisées lors de la réflexion.) Cela mène bien sûr à d'autres parties de la conception étant différente: les collections génériques de C # sont des classes différentes entièrement. que les anciens non génériques. Il est également proche de l'impossibilité de moderniser les génériques dans des cadres hérités; Un exemple notable serait des formulaires Web ASP.NET et ses répéteurs comme un point de douleur laid.


Merci pour l'information. Eh bien, je ne suis pas sûr que si de toute façon est bonne ou non. Personnellement, je n'aime pas l'idée du type brut. Il s'agit d'un trou de trou de la hiérarchie de type bien conçue. Quoi qu'il en soit, la compatibilité est toujours le mal de tête que nous devons faire face à la réalité. :-)


Je trouve qu'il est ironique que vous avez baissé voter ma réponse qui cite un passage de JLS 4.3 qui fait référence au même passage de JLS 4.8 qui constitue la base de votre propre réponse.


@scottb Quelle partie de la JLS devrais-je utiliser alors si la question concerne pourquoi les types bruts sont autorisés, sinon "4.8. types bruts"? Le paragraphe que vous avez cité, d'autre part, est à propos de quelque chose d'autre entièrement, pas d'utilisation de "types bruts", et il est indiqué que ce n'est pas sur les types bruts. Que cela arrive à lier à ce que je pense, c'est que le paragraphe correct est quelque peu accidentel, car c'est la manière dont les travaux de référencement croisé. C'est tout aussi «ironique» que pour une raison quelconque, vous n'aviez pas cliqué sur ce lien pour accéder à une citation qui ne serait pas fausse et trompeuse.


@scottb pour ce que ça vaut la peine, Le paragraphe 5.1.9 sur des conversions non cochées expliquerait les règles de la mission en question dans la question de l'OP. Cependant, cela n'expliquerait pas pourquoi de telles conversions sont autorisées, qui était la question réelle, donc je ne voulais donc pas approfondir en minutiae aléatoire.


@ CS2K C'est très un trou, mais les directives sont que vous ne devriez tout simplement pas utiliser de types bruts dans le nouveau code. Il n'y a aucun moyen pour le compilateur de vous dire et de vous arrêter si vous faites le contraire, mais la seule fois où ils doivent apparaître, c'est si vous utilisez une bibliothèque qui n'a pas été mise à jour pour utiliser des génériques pour une raison quelconque.