Наследование + проблема интерфейса - PullRequest
1 голос
/ 27 октября 2009

У меня есть интерфейс, чтобы описать, когда класс может создать «следующую» версию самого себя:

public interface Prototypeable<Type extends Prototypeable<Type>> {
 public Type basePrototype(); // the zeroth raw instance of Type
 public Type nextPrototype(); // the next instance of Type
}

для использования с

public class Prototyper {
 public static <Type extends Prototypeable<Type>> List<Type> prototypeFactor(int numberOfInstances, Type proto) {
  List<Type> result = new ArrayList<Type>(numberOfInstances);
  Type holder = proto.basePrototype();
  result.add(holder);
  for (int i=1; i<numberOfInstances;i++) result.add(holder = holder.nextPrototype());
  return result;
}

Теперь у меня есть базовый класс A implements Prototypeable<A> и подкласс AButMore extends A. Я хотел бы иметь AButMore extends A implements Prototypeable<AButMore>, но это не разрешено (не может реализовать универсальные интерфейсы несколько раз с разными классами). Также обратите внимание, что A и AButMore оба реализуют некоторые другие интерфейсы, и что реализация идентична от A до AButMore.

Предложения обойти это? Кажется, я не могу разобраться с общей проблемой, поэтому я рассмотрел несколько альтернативных вариантов:

  • псевдо-декорирование обоих классов, т. Е. Наличие базового класса, который не реализует интерфейс Prototypeable, наследование от него в надлежащий подкласс и последующее расширение обоих классов до прототипируемых версий самих себя. Недостатком, кажется, является изобилие классов.

  • не расширяет A до AButMore и вместо этого создает AButMore из A s и делегирует все реплицированные методы. Тем не менее, код делегата всегда кажется мне глупым, особенно когда каждый метод, который может быть унаследован, будет делегирован без изменений.

  • с Prototypeable укажите Object в качестве типа возвращаемого значения и с фабрикой для параметра приведения Class. Недостатком здесь является то, что при неправильном использовании это может привести к небезопасным броскам.

РЕДАКТИРОВАТЬ : Чтобы уточнить: цель состоит в том, чтобы создавать экземпляры, имеющие некоторую последовательную зависимость, без наличия переменной класса. Простейшим примером было бы, если бы у каждого из них была переменная индекса - basePrototype предоставил бы экземпляр с 0 индексами, а nextPrototype () предоставил бы экземпляр с индексом + 1 (на основе индекса экземпляра, из которого был вызван метод). Этот конкретный случай немного упрощен (и, вероятно, может быть реализован более простым способом), но охватывает идею.

РЕДАКТИРОВАТЬ : Для дальнейшего пояснения, вот точная текущая реализация (я использую третий альтернативный вариант выше):

public class BuildFromPrototype {
 public static <T extends Prototypeable> List<T> build(int buildCount, Class<T> protoClass, T prototype) {
  if (protoClass==null || prototype==null || buildCount<=0) return null;
  if( protoClass.isInstance(prototype.basePrototype()) && protoClass.isInstance(prototype.nextPrototype()) ) {
   List<T> result = new ArrayList<T>(buildCount);
   T pHolder = protoClass.cast(prototype.basePrototype());
   result.add(pHolder);
   for (int i=1;i<buildCount;i++)
    result.add(pHolder = protoClass.cast(pHolder.nextPrototype()));
   return result;
  } else return null;
 }

 public interface Prototypeable {
  public Object nextPrototype();
  public Object basePrototype();
 }
}

Я думаю, что это обрабатывает неправильное использование (возврат null является одним из вариантов, Exception также было бы разумно), но тестирование на допустимые приведения может быть дорогостоящим. Эта форма литья также может быть дорогой - я не знаю много о классе Class.

Ответы [ 2 ]

1 голос
/ 27 октября 2009

Я не уверен, что вы хотите сделать с определением «следующих» версий, похоже, вы хотите заниматься программированием метаклассов, чего вы не можете сделать в Java, т.е. я не вижу, как вы можете получить система универсальных типов управляет последовательностью типов, определенных во время выполнения, поскольку они стираются и не существуют во время выполнения. Как насчет интерфейса, который определяет отображение от одного типа к другому, например, что-то вроде

public interface PrototypeMapping<U extends Prototypeable<Type>,V extends U>{
   public V mapTo(U u);
}
0 голосов
/ 29 октября 2009

Единственное отношение, которое можно выразить в обобщениях Java, - это отношение супер / подтип. Для меня это звучит не так, как будто вы хотите больше и больше определенных классов (то есть подтипов), а скорее «родственные» реализации одного и того же интерфейса. Вы не можете выразить это, используя только чисто общие типы - вы не можете аннотировать классы, чтобы Java знала, что MyImplB является «следующей» реализацией для MyImplA.

Самый простой способ передать эту информацию - использовать литерал класса, как вы это делали в последнем случае. Используя это, вы можете строго ограничить тип следующей реализации. Однако, в зависимости от того, что вы хотите сделать, проверка во время выполнения через isInstance, возможно, не самое полезное ограничение; проверка во время компиляции, как правило, будет лучшим вариантом.

В этом случае вам понадобятся два общих параметра в игре - один для типа прототипа и один для класса следующей версии. Исходя из вашего первого фрагмента, звучит так, как будто каждый тип также должен быть параметризован с типом следующей версии:

/**
 * @param N the specific class of the next type
 */
public class/interface Type<N extends Type>
{
   public Type<?> basePrototype();
   public N nextPrototype();
}

public class MyImplA implements Type<MyImplB> { ... }
public class MyImplB implements Type<MyImplC> { ... }
// ... and so on

Это будет статически обеспечивать связь между реализациями различных типов.

Я не уверен, насколько хорошо это принесло бы вам пользу в ситуации, которую вы показали, поскольку не так много поддержки для безопасных по типу гетерогенных контейнеров. Поскольку вы помещаете все в ArrayList, вы не сможете утверждать, что содержимое списка было более конкретным, чем Type<?>. Это помогло бы в обработке отдельных операций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...