На информационном ресурсе применяются рекомендательные технологии (информационные технологии предоставления информации на основе сбора, систематизации и анализа сведений, относящихся к предпочтениям пользователей сети "Интернет", находящихся на территории Российской Федерации)

Мультиметоды в Groovy

Мультиметоды - это механизм выбора метода на основе не только динамического типа его получателя, как при использовании виртуальных методов, но и динамических типов его аргументов. По сути, этот механизм обобщает виртуальные функции и перегрузку.

При правильном использовании этот механизм позволяет писать более компактный и читабельный код, однако, как обычно и бывает, ценой потери производительности.

Хотя вопрос о значительности этих потерь остается открытым, и, я думаю, схож с вопросом о производительности в случае с виртуальных функций.

В Groovy выбор метода для вызова осуществляется на основе динамических типов аргументов, а не статических, как, например, в Java. В частности, рассмотрим два следующих участка кода.

На Java:

class SomeClass {
 
public void doSomething( Object o ) {
System.out.println( "Object" );
}
 
public void doSomething( String s ) {
System.out.println( "String" );
}
}
....
SomeClass someObject = ...
....
Object a1 = new Integer(1);
Object a2 = new String("str");
 
someObject.doSomething( a1 ); // Вызывается первый метод
someObject.doSomething( a2 ); // Вызывается первый метод, статический тип a2 - Object

В результате выполнения этого кода, на консоль будет выведены две строки “Object”.

Теперь, похожий код на Groovy:

class SomeClass {
def doSomething( Object o ) {
println 'Object'
}
 
def doSomething( String o ) {
println 'String'
}
}
....
SomeClass someObject = ...
....
Object a1 = new Integer(1);
Object a2 = new String("str");
 
someObject.doSomething( a1 ); // Вызывается первый метод
someObject.doSomething( a2 ); // Вызывается второй метод, динамический тип a2 - String

В результате выполнения этого кода, на консоль будет выведены две строки: “Object” и “String”

Примеры использования мультиметодов

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

Обработка взаимодействий объектов

Классический пример использования мультиметодов - обработка взаимодействия различных объектов. Например, у нас есть базовый класс SpaceObject и его различные наследники: SpaceShip, Asteroid, Rocket.

Объекты могут сталкиваться. При столкновении, должен вызываться метод collide одного, объекта, у которого тип аргумента - тип второго объекта. В частности, на Groovy, реализация выглядит просто:

class Asteroid implements SpaceObject {
def collide( Asteroid other ) {
// Столкновение астероид - астероид
}
 
def collide( SpaceShip other ) {
// Столкновение астероид - корабль
}
 
def collide( Rocket other ) {
// Столкновение астероид - ракета
}
}

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

SpaceObject o1 = ...
SpaceObject o2 = ...
 
o1.collide( o2 );

В языках, использующих статическую типизацию, например, Java или C++, для реализации подобного поведения необходимо писать в каждом объекте метод-заглушку, который принимает произвольный объект и осуществляет диспетчеризацию. Для этого используется проверка типа во время выполнения (dynamic_cast в C++ и instanceof в Java) или же, если взаимодействие коммутативно, вызовом виртуальной функции аргумента.

Сравнение объектов на равенство

Более часто встречающаяся задача - проверка равенства объектов. Например, метод equals в Java. Обычно его определение выглядит следующим образом:

class SomeClass {
public boolean equals( Object other ) {
if ( other == null ) return false;
if ( !(other instanceof SomeClass) ) return false; // Проверяем класс объекта
 
SomeClass o = (SomeClass)other; // Преобразуем объект к нужному классу
 
... // Собственно, сравнение объектов
}
}

Безусловно, эти лишние три строки в каждом классе не увеличивают читабельность кода. Теперь посмотрим, как это может быть написано на Groovy с учетом мультиметодов.

class SomeClass {
boolean equals( SomeClass other ) {
... // Собственно, сравнение объектов
}
}

Никаких лишних преобразований! Все дело в том, что этот метод будет вызван в случае, если аргумент имеет подходящий динамический тип. В остальных случаях будет вызвана реализация по умолчанию из класса Object, которая вернет false.

Рекомендуем
Популярное
наверх