Статьи

Обобщение в Eclipse Modeling Framework

Java представила мощь дженериков в версии 1.5. Дженерики делают многие общие задачи в программировании не только простыми, но и элегантными. Наиболее заметным примером является задача итерации списков. До Java 1.5 приходилось печатать список в итерации, как показано в листинге 1.

List fruits = new ArrayList(); 
…
for (int i =0; i < fruits.size(); i++) {
  Fruit fruit = (Fruit) fruits.get(i);
  …
}

Листинг 1

При использовании дженериков итерация по списку гораздо менее громоздка. Эквивалентный код показан в листинге 2.

List<Fruit> fruits = new ArrayList<Fruit>();
…
for (Fruit fruit : fruits) {
  …
}

Перечисление 2

В этой статье у нас нет места и времени, чтобы вникать в общий фон дженериков Java. В Интернете есть несколько хороших ссылок, таких как учебник Гилада Брача. Вместо этого мы сконцентрируемся на том, как реализовать обобщенные элементы в Eclipse Modeling Framework (EMF).
Для нашего первого примера мы будем использовать тривиальную модель фруктов и корзин с фруктами. На рисунке 1 показана базовая модель в редакторе Ecore. Объекты Fruit и Basket являются абстрактными. Класс Basket имеет атрибут content, который предоставляет список объектов Fruit.

[img_assist | nid = 2766 | title = Рисунок 1: Базовая модель Ecore | desc = | link = none | align = undefined | width = 407 | height = 216]

Мы бы хотели, чтобы AppleBasket содержал только объекты Apple. Перед генериками у нас было бы два варианта, ни один из них не предпочтителен. Во-первых, мы могли бы перезаписать метод содержимого на AppleBasket, чтобы проверить, что объектом Fruit является Apple. Во-вторых, мы можем создать метод с именем getApples на AppleBasket. Оба подхода сводят на нет преимущества и возможности многократного использования интерфейсов в нашем объектно-ориентированном дизайне.

Чтобы добавить дженерики в нашу модель Ecore, мы должны включить опцию «Показать дженерики». Вы найдете это в меню «Sample Ecore Editor»

 

[img_assist | nid = 2767 | title = Рисунок 2: Включение Ecore для универсальных шаблонов | desc = | link = none | align = undefined | width = 497 | height = 281]

Теперь вы заметите, что контекстное меню в редакторе (щелчок правой кнопкой мыши) имеет больше вариантов.

Мы добавим универсальный в наш класс корзины . Щелкните правой кнопкой мыши класс Basket и выберите «New Child :: Etype Parameter». В представлении свойств дайте параметру простое имя, например «F». Таково соглашение об именах Java-дженериков. Выберите атрибут содержимого и измените его EType на F. На рисунке 3 показаны результаты. Обратите внимание, что модель будет отображать «содержимое: F», но свойства будут отображать «EObject».

[img_assist | nid = 2768 | title = Рисунок 3: Первоначальная декларация об обобщениях | desc = | link = none | align = undefined | width = 485 | height = 367]

Если мы сгенерируем наш код EMF из модели, мы увидим следующее в интерфейсе Basket.

public interface Basket<F> extends EObject {
  public EList<F> getContents();
}

Перечисление 3


Мы хотим обеспечить, чтобы F был типом Fruit . В редакторе Ecore выберите дочерний элемент EType Parameter элемента Basket. Выберите «New Child :: EGeneric Bound Type». В представлении свойств установите для атрибута EClassifier значение Fruit. Теперь имя класса должно отображать «Basket <F extends Fruit>».

[img_assist | nid = 2769 | title = Рисунок 4: Установка общего типа привязки | desc = | link = none | align = undefined | width = 439 | height = 326]

Теперь нам нужно унаследовать эту силу в наших объектах корзины. Разверните детей 
класса
AppleBasket . Вы увидите наследство
корзины . Щелкните правой кнопкой мыши на дочернем объекте наследования,
корзине и выберите в контекстном меню «New Child :: EGeneric Type Argument». Если вы выберите на
AppleBasket, вы не увидите этот пункт меню.

[img_assist | nid = 2770 | title = Рисунок 5: Выбор меню аргумента типа EGeneric | desc = | link = none | align = undefined | width = 590 | height = 112]

В представлении свойств установите для EClassifier значение Apple . Повторите для OrangeBasket . Вы должны иметь то, что показано на рисунке 6.

[img_assist | nid = 2771 | title = Рисунок 6: Аргумент общего типа о наследовании | desc = | link = none | align = undefined | width = 426 | height = 285]

Когда вы генерируете код модели, вы должны увидеть интерфейсы, похожие на листинг 4.

public interface Basket<F extends Fruit> extends EObject {
  public EList<F> getContents();
}

public interface AppleBasket extends Basket<Apple> {
}

Листинг 4

Имея общие шаблоны, вы теперь можете быть настолько специфичными или общими в программировании моделей, насколько захотите. Мы обязались, что объекты AppleBasket могут содержать только объекты Apple . В то же время мы включили доступ к содержимому всех объектов FruitBasket как Fruit . В листинге 5 приведен пример использования модели.

AppleBasket appleBasket = FruitFactory.eINSTANCE.createAppleBasket();
…
for (Apple apple : appleBasket.getContents()){
…
}
List<FruitBasket> baskets;
…
baskets.add(appleBasket);
for (FruitBasket basket: baskets){
  for (Fruit fruit: basket.getContents){
    …
  }
}

Листинг 5