Quelle est la raison pour laquelle Java ne nous permet pas de faire
private T[] elements = new T[initialCapacity];
Je pouvais comprendre que .NET ne nous permettait pas de le faire, car dans .NET, vous avez des types de valeur qui, au moment de l'exécution, peuvent avoir des tailles différentes, mais en Java, toutes sortes de T seront des références d'objet, ayant ainsi la même taille ( Corrige moi si je me trompe).
Quelle est la raison?
java
generics
type-erasure
Elysium dévoré
la source
la source
Réponses:
C'est parce que les tableaux de Java (contrairement aux génériques) contiennent, lors de l'exécution, des informations sur son type de composant. Vous devez donc connaître le type de composant lorsque vous créez le tableau. Comme vous ne savez pas ce qui
T
se passe au moment de l'exécution, vous ne pouvez pas créer le tableau.la source
ArrayList <SomeType>
-il alors?new ArrayList<SomeType>()
? Les types génériques ne contiennent pas le paramètre type lors de l'exécution. Le paramètre type n'est pas utilisé dans la création. Il n'y a aucune différence dans le code généré parnew ArrayList<SomeType>()
ounew ArrayList<String>()
ou pasnew ArrayList()
du tout.ArrayList<T>
fonctionne avec son 'private T[] myArray
. Quelque part dans le code, il doit avoir un tableau de type générique T, alors comment?T[]
. Il a un tableau de type d'exécutionObject[]
, et soit 1) le code source contient une variable deObject[]
(c'est comme ça dans la dernière source Oracle Java); ou 2) le code source contient une variable de typeT[]
, qui est un mensonge, mais ne cause pas de problèmes en raison deT
son effacement à l'intérieur de la portée de la classe.Citation:
(Je crois que c'est Neal Gafter , mais je ne suis pas sûr)
Voir en contexte ici: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316
la source
new T()
. Chaque tableau en Java, de par sa conception, stocke le type de composant (c'estT.class
-à- dire ) à l'intérieur; par conséquent, vous avez besoin de la classe de T au moment de l'exécution pour créer un tel tableau.new Box<?>[n]
, ce qui peut parfois être suffisant, même si cela n'aiderait pas dans votre exemple.Box<String>[] bsa = new Box<String>[3];
est-ce que quelque chose a changé en java-8 et plus je suppose?En omettant de fournir une solution décente, vous vous retrouvez avec quelque chose de pire à mon humble avis.
Le travail commun autour est le suivant.
est remplacé par (en supposant que T étend Object et non une autre classe)
Je préfère le premier exemple, mais plus de types académiques semblent préférer le second, ou préfèrent simplement ne pas y penser.
La plupart des exemples de pourquoi vous ne pouvez pas simplement utiliser un Object [] s'appliquent également à List ou Collection (qui sont pris en charge), donc je les considère comme des arguments très pauvres.
Remarque: c'est l'une des raisons pour lesquelles la bibliothèque de collections elle-même ne compile pas sans avertissements. Si vous ce cas d'utilisation ne peut pas être pris en charge sans avertissements, quelque chose est fondamentalement cassé avec le modèle générique IMHO.
la source
String[]
(ou si vous le stockez dans un champ de type accessible au publicT[]
, et que quelqu'un le récupère), alors il obtiendra une exception ClassCastException.T[] ts = (T[]) new Object[n];
est une mauvaise idée: stackoverflow.com/questions/21577493/…T[] ts = new T[n];
c'était un exemple valable. Je vais garder le vote car sa réponse peut causer des problèmes et des confusions à d'autres développeurs et est également hors sujet. Aussi, je vais arrêter de commenter à ce sujet.Les tableaux sont covariants
Cette dernière ligne se compilerait très bien, mais si nous exécutons ce code, nous obtiendrions un
ArrayStoreException
car nous essayons de mettre un double dans un tableau d'entiers. Le fait que nous accédions au tableau via une référence numérique n'est pas pertinent ici, ce qui importe, c'est que le tableau est un tableau d'entiers.Cela signifie que nous pouvons tromper le compilateur, mais nous ne pouvons pas tromper le système de type d'exécution. Et c'est parce que les tableaux sont ce que nous appelons un type réifiable. Cela signifie qu'au moment de l'exécution, Java sait que ce tableau a été réellement instancié comme un tableau d'entiers auquel il est simplement possible d'accéder via une référence de type
Number[]
.Donc, comme nous pouvons le voir, une chose est le type réel de l'objet, une autre chose est le type de référence que nous utilisons pour y accéder, non?
Le problème avec les génériques Java
Maintenant, le problème avec les types génériques en Java est que les informations de type pour les paramètres de type sont ignorées par le compilateur après la compilation du code; par conséquent, ces informations de type ne sont pas disponibles au moment de l'exécution. Ce processus est appelé effacement de type . Il existe de bonnes raisons pour implémenter des génériques comme celui-ci en Java, mais c'est une longue histoire, et cela a à voir avec la compatibilité binaire avec du code préexistant.
Considérons maintenant le code dangereux suivant:
Si le compilateur Java ne nous empêche pas de le faire, le système de type d'exécution ne peut pas nous arrêter non plus, car il n'y a aucun moyen, au moment de l'exécution, de déterminer que cette liste était censée être uniquement une liste d'entiers. L'exécution Java nous permettrait de mettre tout ce que nous voulons dans cette liste, alors qu'elle ne devrait contenir que des entiers, car lorsqu'elle a été créée, elle a été déclarée comme une liste d'entiers. C'est pourquoi le compilateur rejette la ligne numéro 4 car elle n'est pas sûre et si elle est autorisée, elle pourrait casser les hypothèses du système de type.
En tant que tel, les concepteurs de Java se sont assurés que nous ne pouvons pas tromper le compilateur. Si nous ne pouvons pas tromper le compilateur (comme nous pouvons le faire avec les tableaux), nous ne pouvons pas non plus tromper le système de type d'exécution.
En tant que tels, nous disons que les types génériques ne sont pas réifiables, car au moment de l'exécution, nous ne pouvons pas déterminer la véritable nature du type générique.
J'ai ignoré certaines parties de ces réponses, vous pouvez lire l'article complet ici: https://dzone.com/articles/covariance-and-contravariance
la source
La raison pour laquelle cela est impossible est que Java implémente ses génériques uniquement au niveau du compilateur et qu'un seul fichier de classe est généré pour chaque classe. C'est ce qu'on appelle l' effacement de type .
Au moment de l'exécution, la classe compilée doit gérer toutes ses utilisations avec le même bytecode. Donc,
new T[capacity]
n'aurait absolument aucune idée du type à instancier.la source
La réponse a déjà été donnée, mais si vous avez déjà une instance de T, vous pouvez le faire:
J'espère que je pourrais aider, Ferdi265
la source
T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;
.T[] t
, ce serait le cas(T[]) Array.newInstance(t.getClass().getComponentType(), length);
. j'ai passé quelques temps à comprendregetComponentType()
. J'espère que cela aide les autres.t.clone()
ne reviendra pasT[]
. Parce quet
n'est pas Array dans cette réponse.La raison principale est due au fait que les tableaux en Java sont covariants.
Il y a un bon aperçu ici .
la source
String[]
àObject
et stocker unInteger
dedans. Ils ont donc dû ajouter une vérification du type d'exécution pour les magasins de tableaux (ArrayStoreException
) car le problème ne pouvait pas être détecté au moment de la compilation. (Sinon, unInteger
pourrait réellement être coincé dans unString[]
, et vous obtiendriez une erreur lorsque vous essayez de le récupérer, ce qui serait horrible.) ...Object
aurait dû êtreObject[]
dans mon premier commentaire.J'aime la réponse indirectement donnée par Gafter . Cependant, je propose que ce soit faux. J'ai un peu changé le code de Gafter. Il compile et il fonctionne pendant un certain temps, puis il bombarde là où Gafter avait prédit
La sortie est
Il me semble donc que vous pouvez créer des types de tableaux génériques en java. Ai-je mal compris la question?
la source
Je sais que je suis un peu en retard à la fête ici, mais je me suis dit que je pourrais peut-être aider les futurs googleurs car aucune de ces réponses n'a résolu mon problème. La réponse de Ferdi265 a énormément aidé.
J'essaie de créer ma propre liste liée, donc le code suivant est ce qui a fonctionné pour moi:
Dans la méthode toArray () se trouve la façon de créer un tableau d'un type générique pour moi:
la source
Dans mon cas, je voulais simplement un tableau de piles, quelque chose comme ceci:
Comme cela n'était pas possible, j'ai utilisé ce qui suit comme solution:
Moche, mais Java est content.
Remarque: comme mentionné par BrainSlugs83 dans le commentaire de la question, il est totalement possible d'avoir des tableaux de génériques dans .NET
la source
À partir du didacticiel Oracle :
Pour moi, cela semble très faible. Je pense que quiconque ayant une compréhension suffisante des génériques serait parfaitement bien, et même s'attendrait à ce que l'arrayStoredException ne soit pas levée dans ce cas.
la source
Il doit sûrement y avoir un bon moyen de contourner cela (peut-être en utilisant la réflexion), car il me semble que c'est exactement ce qui
ArrayList.toArray(T[] a)
fait. Je cite:Donc, une solution consiste à utiliser cette fonction, c'est-à-dire à créer un
ArrayList
des objets que vous souhaitez dans le tableau, puis à utilisertoArray(T[] a)
pour créer le tableau réel. Ce ne serait pas rapide, mais vous n'avez pas mentionné vos besoins.Alors, quelqu'un sait comment
toArray(T[] a)
est mis en œuvre?la source
Array.newInstance()
. Vous trouverez cela mentionné dans de nombreuses questions qui demandent comment créer un tableau avec un type inconnu au moment de la compilation. Mais l'OP demandait spécifiquement pourquoi vous ne pouvez pas utiliser lanew T[]
syntaxe, ce qui est une question différenteC'est parce que les génériques ont été ajoutés à java après leur création, donc c'est un peu maladroit parce que les fabricants originaux de java pensaient que lors de la création d'un tableau, le type serait spécifié lors de sa création. Donc, cela ne fonctionne pas avec les génériques, vous devez donc faire E [] array = (E []) new Object [15]; Ceci compile mais il donne un avertissement.
la source
Si nous ne pouvons pas instancier des tableaux génériques, pourquoi le langage a-t-il des types de tableaux génériques? Quel est l'intérêt d'avoir un type sans objet?
La seule raison pour laquelle je peux penser, c'est varargs -
foo(T...)
. Sinon, ils auraient pu complètement nettoyer les types de tableaux génériques. (Eh bien, ils n'avaient pas vraiment besoin d'utiliser un tableau pour les varargs, car les varargs n'existaient pas avant la 1.5 . C'est probablement une autre erreur.)C'est donc un mensonge, vous pouvez instancier des tableaux génériques, via varargs!
Bien sûr, les problèmes avec les tableaux génériques sont toujours réels, par exemple
Nous pouvons utiliser cet exemple pour réellement démontrer le danger d' un tableau générique .
D'un autre côté, nous utilisons des varargs génériques depuis une décennie, et le ciel ne tombe pas encore. Nous pouvons donc affirmer que les problèmes sont exagérés; ce n'est pas grave. Si la création explicite de tableaux génériques est autorisée, nous aurons des bugs ici et là; mais nous avons été habitués aux problèmes d'effacement, et nous pouvons vivre avec.
Et nous pouvons pointer du
foo2
doigt pour réfuter l'affirmation selon laquelle la spécification nous empêche des problèmes dont ils prétendent nous empêcher. Si Sun avait eu plus de temps et de ressources pour 1.5 , je pense qu'ils auraient pu atteindre une résolution plus satisfaisante.la source
Comme d'autres l'ont déjà mentionné, vous pouvez bien sûr créer via quelques astuces .
Mais ce n'est pas recommandé.
Parce que l' effacement de type et, plus important encore, le
covariance
tableau in qui autorise simplement un tableau de sous-types peuvent être attribués à un tableau de supertype, ce qui vous oblige à utiliser un transtypage de type explicite lorsque vous essayez de récupérer la valeur, ce qui provoque l'exécution,ClassCastException
ce qui est l'un des principaux objectifs que les génériques essaient d'éliminer: des contrôles de type plus forts au moment de la compilation .Un exemple plus direct peut être trouvé dans Effective Java: Item 25 .
covariance : un tableau de type S [] est un sous-type de T [] si S est un sous-type de T
la source
Si la classe utilise comme type paramétré, elle peut déclarer un tableau de type T [], mais elle ne peut pas instancier directement un tel tableau. Au lieu de cela, une approche courante consiste à instancier un tableau de type Object [], puis à effectuer un transtypage réduit en type T [], comme illustré ci-dessous:
la source
Essaye ça:
la source