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