1. Обзор
Иногда мы можем задаться вопросом, можем ли мы добавить несколько дополнительных удобных методов в скомпилированные классы Java или Groovy, если у нас нет возможности изменять исходный код. Как оказалось, категория Groovy позволяет нам сделать именно это.
Groovy — это динамичный и мощный язык JVM с многочисленными функциями метапрограммирования .
В этом руководстве мы рассмотрим концепцию категорий в Groovy.
2. Что такое категория?
Категории — это функция метапрограммирования, вдохновленная Objective-C, которая позволяет нам добавлять дополнительные функции в новый или существующий класс Java или Groovy.
В отличие от расширений , дополнительные функции, предоставляемые категорией, не включены по умолчанию. Таким образом, ключом к включению категории является использование
блока кода.
Дополнительные функции, реализованные категорией, доступны только внутри блока кода использования .
3. Категории в Groovy
Давайте обсудим несколько известных категорий, которые уже доступны в Groovy Development Kit.
3.1. Категория времени
Класс TimeCategory
доступен в пакете groovy.time
, который добавляет несколько удобных способов работы с объектами даты
и времени .
Эта категория добавляет возможность конвертировать целое число
во временную запись, такую как секунды, минуты, дни и месяцы .
Кроме того, класс TimeCategory
предоставляет такие методы, как plus
и minus
, для простого добавления Duration
к объектам Date
и вычитания Duration
из объектов Date
соответственно.
Давайте рассмотрим несколько удобных функций, предоставляемых классом TimeCategory
. Для этих примеров мы сначала создадим объект Date
, а затем выполним несколько операций с помощью TimeCategory
:
def jan_1_2019 = new Date("01/01/2019")
use (TimeCategory) {
assert jan_1_2019 + 10.seconds == new Date("01/01/2019 00:00:10")
assert jan_1_2019 + 20.minutes == new Date("01/01/2019 00:20:00")
assert jan_1_2019 - 1.day == new Date("12/31/2018")
assert jan_1_2019 - 2.months == new Date("11/01/2018")
}
Давайте подробно обсудим код.
Здесь 10.seconds
создает объект TimeDuration
со значением 10 секунд. И оператор плюс (+) добавляет объект TimeDuration к объекту
Date
.
Аналогично, 1.day
создает объект Duration
со значением 1 день. И оператор минус (-) вычитает объект Duration из объекта
Date
.
Кроме того, несколько методов, таких как now
, ago
и from
, доступны через класс TimeCategory
, который позволяет создавать относительные даты .
Например, 5.days.from.now
создаст объект Date
со значением на 5 дней раньше текущей даты. Точно так же 2.hours.ago
устанавливает значение на 2 часа раньше текущего времени.
Давайте посмотрим на них в действии. Кроме того, мы будем использовать SimpleDateFormat
, чтобы игнорировать границы времени при сравнении двух похожих объектов Date :
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy")
use (TimeCategory) {
assert sdf.format(5.days.from.now) == sdf.format(new Date() + 5.days)
sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss")
assert sdf.format(10.minutes.from.now) == sdf.format(new Date() + 10.minutes)
assert sdf.format(2.hours.ago) == sdf.format(new Date() - 2.hours)
}
Поэтому, используя класс TimeCategory
, мы можем писать простой и более читаемый код, используя уже известные нам классы.
3.2. DOMКатегория
Класс DOMCategory
доступен в пакете groovy.xml.dom
. Он предлагает несколько удобных способов работы с DOM-объектом Java.
В частности, DOMCategory
позволяет выполнять операции GPath с элементами DOM, упрощая обход и обработку XML-файлов .
Сначала напишем простой XML-текст и разберем его с помощью класса DOMBuilder
:
def foreachArticlesText = """
<articles>
<article core-java="true">
<title>An Intro to the Java Debug Interface (JDI)</title>
<desc>A quick and practical overview of Java Debug Interface.</desc>
</article>
<article core-java="false">
<title>A Quick Guide to Working with Web Services in Groovy</title>
<desc>Learn how to work with Web Services in Groovy.</desc>
</article>
</articles>
"""
def foreachArticlesDom = DOMBuilder.newInstance().parseText(foreachArticlesText)
def root = foreachArticlesDom.documentElement
Здесь корневой
объект содержит все дочерние узлы DOM. Давайте пройдемся по этим узлам, используя класс DOMCategory
:
use (DOMCategory) {
assert root.article.size() == 2
def articles = root.article
assert articles[0].title.text() == "An Intro to the Java Debug Interface (JDI)"
assert articles[1].desc.text() == "Learn how to work with Web Services in Groovy."
}
Здесь класс DOMCategory обеспечивает
легкий доступ к узлам и элементам с помощью точечных операций , предоставляемых GPath
. Кроме того, он предоставляет такие методы, как размер
и текст
, для доступа к информации о любом узле или элементе .
Теперь давайте добавим новый узел к корневому
объекту DOM, используя DOMCategory
:
use (DOMCategory) {
def articleNode3 = root.appendNode(new QName("article"), ["core-java": "false"])
articleNode3.appendNode("title", "Metaprogramming in Groovy")
articleNode3.appendNode("desc", "Explore the concept of metaprogramming in Groovy")
assert root.article.size() == 3
assert root.article[2].title.text() == "Metaprogramming in Groovy"
}
Точно так же класс DOMCategory
также содержит несколько методов, таких как appendNode
и setValue
, для изменения DOM .
4. Создайте категорию
Теперь, когда мы увидели несколько категорий Groovy в действии, давайте рассмотрим, как создать пользовательскую категорию.
4.1. Использование собственного объекта
Класс категории должен следовать определенным правилам для реализации дополнительных функций.
Во-первых, метод добавления дополнительной функции должен быть статическим
. Во-вторых, первым аргументом метода должен быть объект, к которому применима эта новая функция.
Давайте добавим функцию использования заглавных букв
в класс String .
Это просто изменит первую букву строки
на прописную.
Сначала мы напишем класс ForEachCategory
со статическим
методом, использующим заглавные буквы
, и типом String
в качестве первого аргумента:
class ForEachCategory {
public static String capitalize(String self) {
String capitalizedStr = self;
if (self.size() > 0) {
capitalizedStr = self.substring(0, 1).toUpperCase() + self.substring(1);
}
return capitalizedStr
}
}
Затем давайте напишем быстрый тест, чтобы включить ForEachCategory
и проверить функцию использования заглавных букв
в объекте String
:
use (ForEachCategory) {
assert "norman".capitalize() == "Norman"
}
Точно так же давайте напишем функцию для возведения числа в степень другого числа:
public static double toThePower(Number self, Number exponent) {
return Math.pow(self, exponent);
}
Наконец, давайте проверим нашу пользовательскую категорию:
use (ForEachCategory) {
assert 50.toThePower(2) == 2500
assert 2.4.toThePower(4) == 33.1776
}
4.2. @категория
аннотации
Мы также можем использовать аннотацию @groovy.lang.Category
, чтобы объявить категорию как класс в стиле экземпляра . При использовании аннотации мы должны указать имя класса, к которому применима наша категория.
Экземпляр объекта доступен с помощью этого
ключевого слова в методе. Следовательно, объект self
не обязательно должен быть первым аргументом.
Давайте напишем класс NumberCategory
и объявим его как категорию с аннотацией @Category
. Кроме того, мы добавим в нашу новую категорию несколько дополнительных функций, таких как куб
и разделять с округлением вверх:
@Category(Number)
class NumberCategory {
public Number cube() {
return this*this*this
}
public int divideWithRoundUp(BigDecimal divisor, boolean isRoundUp) {
def mathRound = isRoundUp ? BigDecimal.ROUND_UP : BigDecimal.ROUND_DOWN
return (int)new BigDecimal(this).divide(divisor, 0, mathRound)
}
}
В данном случае функцияdivideWithRoundUp
делит число на делитель и округляет результат вверх/вниз до следующего или предыдущего целого числа на основе параметра isRoundUp
.
Давайте протестируем нашу новую категорию:
use (NumberCategory) {
assert 3.cube() == 27
assert 25.divideWithRoundUp(6, true) == 5
assert 120.23.divideWithRoundUp(6.1, true) == 20
assert 150.9.divideWithRoundUp(12.1, false) == 12
}
5. Вывод
В этой статье мы рассмотрели концепцию категорий в Groovy — функцию метапрограммирования, которая может включать дополнительные функции в классах Java и Groovy.
Мы рассмотрели несколько категорий, таких как TimeCategory
и DOMCategory,
которые уже доступны в Groovy .
В то же время мы изучили несколько дополнительных удобных способов работы с Date
и DOM Java с использованием этих категорий.
Наконец, мы рассмотрели несколько способов создания собственной пользовательской категории.
Как обычно, все реализации кода доступны на GitHub .