开发者

Java: Instantiating a generic class with no default constructor

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 quest开发者_如何学编程ion 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.


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


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.


You need to use Factory interface:

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

also you need to make a factory for each class "with 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);
   }
}

Your code will look like:

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


You can call it like:

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


P.S. It's not my original solution. I have found it somewhere long time ago and used it in my code. Unfortunately I can't find a link to an original idea...


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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜