views:

682

answers:

3

Hi,

I am trying to do this:

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
        }
    }
}

Instantiating generics is hard enough as it is, but what makes this even harder is that T here does not have a default constructor, it takes a single int parameter in its constructor.

How can this be done?


I have asked a follow up question here too. I'd be grateful if you could answer that as well.

This question is related, but only is relevant where the classes are assumed to have a default constructor.

+1  A: 

The good reason why it shouldn't be done like this is, that you can't force a sub class to implement a certain constructor. It's very obvious for implementations of interface, for they don't include a contract for constructors.

And if your real or abstract class BaseTable had a constructor BaseTable(int columns), the fictious sub class VectorTable with a single column wouldn't need to implement it and could do something bad like

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

So first, you don't know if T implements the constructor at all and second, you don't know if the constructor has the same purpose (which it really should have in proper code!!)

So I think, a better solution is to move the parametrized part from the constructor into a separate (final?) method, create the instance with the default constructor and call that initialization method right afterwards.

You may think of implementing a BaseTableFactory if you want to make sure, that all BaseTable sub classes are always initialized correctly.

Edit

And new T() (instantiating the default constructor) isn't possible as well, because no class can be forced to implement an accessible default constructor. I still think, the factory pattern is your best friend here.

Andreas_D
**And**, `T` can be an abstract class or interface. In which case, it could not be instantiated *directly* under any circumstances.
Andrzej Doyle
+4  A: 

It was already said, that you can't create an instance of T with new, so I would use the Factory Pattern or a Prototype Pattern

So your constructor would look like public BaseTable(int rows, int cols, LineFactory factory) with an appropriate instance of a factory.

In your case, I would prefer the Prototype Pattern, because your TableEntry objects are probably very light-weight. Your code would look like:

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());
}
Zappi
A: 

Simple!

Use a static factory method instead of a constructor:

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

To instantiate type

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