Java: создание универсального класса без конструктора по умолчанию - PullRequest
4 голосов
/ 21 января 2010

Я пытаюсь сделать это:

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

Создание обобщенных шаблонов достаточно сложно, но это еще более усложняет то, что T здесь не имеет конструктора по умолчанию, в его конструкторе используется один параметр int.

Как это можно сделать?


Я также задал следующий вопрос и здесь. Буду признателен, если вы ответите и на это.

Этот вопрос связан, но он актуален только в том случае, если предполагается, что классы имеют конструктор по умолчанию.

Ответы [ 4 ]

5 голосов
/ 21 января 2010

Уже было сказано, что вы не можете создать экземпляр T с new, поэтому Я бы использовал Фабричный шаблон или Шаблон прототипа

Так ваш конструктор будет выглядеть public BaseTable(int rows, int cols, LineFactory factory) с соответствующим экземпляром фабрики.

В вашем случае я бы предпочел Шаблон прототипа, потому что ваши объекты TableEntry, вероятно, очень легкие. Ваш код будет выглядеть так:

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 голосов
/ 21 января 2010

Хорошая причина, по которой это не должно быть сделано так, состоит в том, что вы не можете заставить подкласс реализовать определенный конструктор. Это очень очевидно для реализаций интерфейса, поскольку они не включают контракт для конструкторов.

И если бы у вашего реального или абстрактного класса BaseTable был конструктор BaseTable(int columns), вымышленный подкласс VectorTable с одним столбцом не потребовал бы его реализации и мог бы сделать что-то плохое, например

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

Итак, во-первых, вы не знаете, реализует ли T конструктор вообще, а во-вторых, вы не знаете, имеет ли конструктор ту же цель (, которую он действительно должен иметь в надлежащем коде !! )

Так что я думаю, что лучшее решение - переместить параметризованную часть из конструктора в отдельный (конечный?) Метод, создать экземпляр с конструктором по умолчанию и сразу же вызвать этот метод инициализации.

Вы можете подумать о реализации BaseTableFactory, если хотите убедиться, что все подклассы BaseTable всегда инициализируются правильно.

Редактировать

И new T() (создание конструктора по умолчанию) также невозможны, поскольку ни один класс не может быть принудительным для реализации доступного конструктора по умолчанию. Я все еще думаю, что фабричный образец - ваш лучший друг здесь.

1 голос
/ 14 сентября 2012

Вам необходимо использовать интерфейс Factory:

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

также вам нужно сделать фабрику для каждого класса "с типом Т":

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

Ваш код будет выглядеть так:

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


Вы можете назвать это как:

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


P.S. Это не мое оригинальное решение. Я нашел это где-то давным-давно и использовал в своем коде. К сожалению, я не могу найти ссылку на оригинальную идею ...

0 голосов
/ 22 января 2010

Simple!

Используйте статический фабричный метод вместо конструктора:

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

Для создания экземпляра типа

BaseTable<Number> = BaseTable.newInstance(10, 20);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...