Java: creating a generic class without a 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 } } } 

Creating generic copies is quite complicated, but it makes it even harder that T does not have a default constructor here, it takes a single int parameter in its constructor.

How can I do that?


I also asked the following question . I would appreciate it if you would answer that too.

This question is related, but only matters where it is assumed that classes have a default constructor.

+4
source share
4 answers

It has already been said that you cannot create an instance of T with new , so I would use the Factory Pattern or Prototype Pattern

So, your constructor will look like this: public BaseTable(int rows, int cols, LineFactory factory) with the corresponding factory instance.

In your case, I would prefer the Prototype pattern because your TableEntry objects are probably very lightweight. Your code will look like this:

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

A good reason why this should not be done is that you cannot force a subclass to execute a specific constructor. This is very obvious for the implementation of the interface, since they do not include a contract for designers.

And if your real or abstract BaseTable class had a BaseTable(int columns) constructor, a dummy VectorTable subclass with one column would not have to implement it and could do something bad, for example

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

So, you don’t know if T implements the constructor at all, and secondly, you don’t know if the constructor has the same goal (which it really should have in the correct code !!)

So, the best solution is to move the parameterized part from the constructor to a separate (final?) Method, create an instance with the default constructor and immediately call this initialization method.

You might consider implementing a BaseTableFactory if you want to make sure that all subclasses of BaseTable are always initialized correctly.

Edit

And new T() (instantiating the default constructor) is also impossible, since no class can be forced to be implemented by the available default constructor. I still think the factory pattern is your best friend here.

+3
source

You need to use the Factory interface:

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

also you need to do 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 this:

 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 this:

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


PS This is not my original decision. I found it somewhere long ago and used it in my code. Unfortunately, I can not find a link to the original idea ...

+1
source

Simple!

Use the static factory method instead of the constructor:

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

To create an instance of type

 BaseTable<Number> = BaseTable.newInstance(10, 20); 
-one
source

Source: https://habr.com/ru/post/1298958/


All Articles