2009-11-04 5 views
2

In. NET, je peux faire quelque chose comme ceci:Génériques et clause "where"

public static T[] CreateAndFillArray<T>(int size) where T : new() 
{ 
    T[] array = new T[size]; 
    for (int i = size - 1; i >= 0; i--) 
     array[i] = new T(); 
    return array; 
} 

Nous devons spécifier "où T: nouvelle()" clause.

Comment le faire en Java?

+0

qu'est-ce que * où T: new() * fait? – OscarRyz

+4

@Oscar Reyes: 'où T: new()' signifie que 'T' doit avoir un constructeur public sans paramètre. Référence: http://msdn.microsoft.com/en-us/library/d5x73970.aspx –

Répondre

3

En Java, vous ne pouvez pas instancier un tableau générique. (par exemple, le nouveau T [size] ne peut pas fonctionner) Cela s'explique par le fait que les types de génériques sont perdus lors de l'exécution ("effacement") et ne peuvent pas être récupérés.

Y a-t-il une raison pour laquelle vous ne pouvez pas utiliser, par exemple, le nouveau ArrayList <T>()?

+0

Bien sûr, je peux utiliser ArrayList. Je voulais savoir si je pouvais le faire en Java;) –

+1

@mykhaylo: Hein? 'ArrayList' * fait * partie de Java.Et 'ArrayList' ne résoudra pas tous vos problèmes, sauf si vous changez votre fonction pour accepter un' ArrayList' en tant que paramètre. Tout comme vous ne pouvez pas faire 'new T [size]', vous ne pouvez pas faire 'new ArrayList ()' non plus, puisque 'T' est effacé lors de l'exécution. –

+0

@Daniel - vous ** pouvez ** faire 'new ArrayList ()'; vous ne pouvez pas le remplir avec de nouvelles instances de T sans passer le paramètre 'Class ' comme paramètre. – ChssPly76

11

Vous ne pourrez pas créer un tableau de type "T" générique dans java à moins que vous ne transmettiez T comme paramètre.

public static <T> T[] createAndFillArray(Class<T> cls, int size) { 
    T[] result = (T[]) Array.newInstance(cls, size); 
    for (int i=0; i<size; i++) { 
    result[i] = cls.newInstance(); 
    } 
    return result; 
} 
+2

Correct. Contrairement à C#, Java n'a pas de génériques réifiés. Donc, le type réel de 'T' n'est pas disponible pour la construction de nouveaux objets. –

+2

Il est intéressant de noter que CLR fait un truc très similaire à faire 'new T()' (et 'typeof (T)', et quelques autres choses similaires où il doit vraiment connaître 'T') - il ajoute un caché paramètre de méthode pour le type handle sous le capot. La seule différence est qu'elle n'est visible ni sur C# ni sur le niveau IL - c'est un détail d'implémentation de l'exécution. –

3

Java n'a pas de construction équivalente. Il n'y a pas de sécurité de compilation sur une classe contenant un constructeur.

Vous pouvez le faire lors de l'exécution, mais vous devez soit passer un T non nul ou une classe en tant que paramètre. Le paramètre de type utilisé n'est pas conservé lors de l'exécution. Ce qui précède fonctionnera mais lancera une exception s'il n'y a pas de constructeur public sans argument. Vous ne pouvez pas obtenir le compilateur pour appliquer qu'il y en a un. Edit: ChssPly76 m'a battu, donc j'ai modifié le code ci-dessus pour donner un exemple où vous passez dans un échantillon d'objet réel, juste pour montrer comment cela est fait. Normalement, dans un tel cas, vous passez dans la classe car l'objet sampleObject ne se retrouve pas dans le tableau.

0

Vous ne pouvez pas le faire en Java, car Java ne prend pas en charge les vérifications de type structurel.

Scala fait, mais c'est beaucoup plus lent que l'implémentation d'une interface appropriée (car il utilise la réflexion interne pour faire les appels de fonction). Scala ne vous permet pas de définir des contraintes sur la forme du constructeur de l'objet. La JVM utilise l'effacement de type, de sorte que le code générique ne sait pas réellement sur quel type il fonctionne, donc il ne peut pas construire de nouveaux objets de ce type quand même.

1

Vous pouvez utiliser cette idée pour corriger le manque de temps de compilation vérifier dans les autres réponses:

import java.lang.reflect.Array; 

public class Main 
{ 
    public static void main(String[] args) 
    { 
     final String[] array; 

     array = createAndFillArray(String.class, 10, new StringCreator()); 

     for(final String s : array) 
     { 
      System.out.println(s); 
     } 
    } 

    public static <T> T[] createAndFillArray(final Class<T> clazz, 
              final int  size, 
              final Creator<T> creator) 
    { 
     T[] result = (T[]) Array.newInstance(clazz, size); 

     for (int i=0; i<size; i++) 
     { 
      result[i] = creator.newInstance(); 
     } 

     return result; 
    } 
} 

interface Creator<T> 
{ 
    T newInstance(); 
} 

class StringCreator 
    implements Creator<String> 
{ 
    public String newInstance() 
    { 
     // not the best example since String is immutable but you get the idea 
     // you could even have newInstance take an int which is the index of the 
     // item being created if that could be useful (which it might). 
     return ("hello"); 
    } 
} 

Ceci est en fait plus souple que la façon dont C# vous décrivez puisque vous pouvez contrôler le constructeur si vous voulez plutôt que d'appeler simplement le No-Arg.