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