2010-01-21 5 views
4

Je suis en train de le faire:Java: instanciation d'une classe générique sans constructeur par défaut

public class BaseTable<T extends TableEntry> 

{ 

    protected int mRows; 
    protected int mCols; 
    protected ArrayList<T> mEntries; 

    public BaseTable(int rows, int cols) 
    { 
     mRows = rows; 
     mCols = cols; 
     mEntries = new ArrayList<T>(); 
     for (int i = 0; i < rows; i++) 
     { 
      mEntries.add(new T(cols)); //this obv. doesn't work 
     } 
    } 
} 

génériques instanciation est assez difficile comme il est, mais ce qui rend encore plus difficile est que T ici n'a pas constructeur par défaut, il prend un seul paramètre int dans son constructeur.

Comment cela peut-il être fait? J'ai également demandé un follow up question ici aussi. Je vous serais reconnaissant de bien vouloir répondre à cette question également.

Cette question est liée, mais n'est pertinente que si les classes sont supposées avoir un constructeur par défaut.

Répondre

5

On a déjà dit que vous ne pouvez pas créer une instance de T avec new, donc je voudrais utiliser le modèle d'usine ou d'un motif Prototype

donc votre constructeur ressemblerait public BaseTable(int rows, int cols, LineFactory factory) avec une instance appropriée d'une usine.

Dans votre cas, je préférerais le motif prototype, car vos objets TableEntry sont probablement très légers. Votre code ressemblerait à:

public BaseTable(int rows, int cols, T prototype) 
{  
    mRows = rows; 
    mCols = cols; 
    prototype.setColumns(cols); 
    mEntries = new ArrayList<T>(); 
    for (int i = 0; i < rows; i++) 
    { 
    @SuppressWarnings("unchecked") 
    T newClone = (T)prototype.clone(); 
    mEntries.add(newClone); //this obv. does work :) 
    } 
} 

public static void main(String[] args) 
{ 
    new BaseTable<SimpleTableEntry>(10, 2, new SimpleTableEntry()); 
} 
3

La bonne raison pour laquelle ne devrait pas être être fait comme ceci, c'est que vous ne pouvez pas forcer une sous-classe à implémenter un certain constructeur. C'est très évident pour les implémentations d'interface, car elles n'incluent pas de contrat pour les constructeurs.

Et si votre vrai ou classe abstraite BaseTable avait un constructeur BaseTable(int columns), la sous classe VectorTable avec une fictive colonne unique ne serait pas nécessaire de mettre en œuvre et pourrait faire quelque chose de mal comme

public VectorTable(int intialValue) { 
    super(1); 
    this.initialValue = initialValue; 
} 

Alors d'abord, vous ne sais pas si T met en œuvre le constructeur du tout et, deuxièmement, vous ne savez pas si le constructeur a le même but (dont il devrait vraiment avoir dans le code approprié !!)

Je pense, mieux solution i s pour déplacer la partie paramétrée du constructeur dans une méthode séparée (finale?), créer l'instance avec le constructeur par défaut et appeler cette méthode d'initialisation juste après.

Vous pouvez penser à implémenter un BaseTableFactory si vous voulez vous assurer que toutes les sous-classes BaseTable sont toujours initialisées correctement.

Modifier

Et new T() (instancier le constructeur par défaut) n'est pas possible, parce qu'aucune classe ne peut être contraint à mettre en œuvre un constructeur par défaut accessible. Je pense toujours, le modèle d'usine est votre meilleur ami ici.

+1

** et **, 'T' peut être une classe abstraite ou de l'interface. Dans ce cas, il ne pouvait pas être instancié * directement * en toutes circonstances. –

-1

Simple!

Utilisez une méthode d'usine statique au lieu d'un constructeur:

public static <T extends TableEntry> newInstance(int rows, int cols) { 
    return new BaseTable<T>(rows, cols); 
} 

Pour instancier Type

BaseTable<Number> = BaseTable.newInstance(10, 20); 
1

Vous devez utiliser l'interface usine:

public interface TableEntryFactory<T extends TableEntry>{ 
public T create (int cols); 
} 

aussi vous devez faire une usine pour chaque classe « avec le type T »:

// for every class of type T 
public class SpecialTableEntry extends TableEntry{ 
    SpecialTableEntry(int cols){ 
     ... 
    } 
} 

// make a factory creating instance of this class 
public class SpecialTableEntryFactory implements TableEntryFactory<SpecialTableEntry> { 
    @Override 
    public SpecialTableEntry create (int cols){ 
     return new SpecialTableEntry(cols); 
    } 
} 

Votre code ressemblera:

public class BaseTable<T extends TableEntry> 

{ 

protected int mRows; 
protected int mCols; 
protected ArrayList<T> mEntries; 

public BaseTable(int rows, int cols, TableEntryFactory<T> tableFactory) 
{ 
    mRows = rows; 
    mCols = cols; 
    mEntries = new ArrayList<T>(); 
    for (int i = 0; i < rows; i++) 
     { 
     mEntries.add(tableFactory.create(cols)); //this should work now 
     } 
    } 
} 


Vous pouvez l'appeler comme:

TableEntryFactory<SpecificTableEntry> myFactory = new SpecificTableEntryFactory(); 
BaseTable<?> table = new BaseTable<SpecificTableEntry>(rows,cols,myFactory); 


post-scriptum Ce n'est pas ma solution originale. Je l'ai trouvé il y a quelque part le temps long et utilisé dans mon code. Malheureusement, je ne peux pas trouver un lien vers une idée originale ...

Questions connexes