Пишу на котлин, и возникла такая ситуация.
Есть набор разных классов (можно считать, data-классов), одинаковых по структуре, т.е. у них часть полей одинаковы по названиям и типам.
Нужно с объектами этих классов делать однотипные действия с "общими" полями.
На плюсах я бы сделал это шаблонными функциями.
Есть ли аналог шаблонных функций в котлине?
class A {
int value;
}
class B {
int value;
}
template <typename T>
void printValue(T t) {
std::cout << t.value << std::endl;
}
Как подобное изобразить на котлине ?
Почему нельзя вынести одинаковые поля в общий класс?
Классы авто-генерируются по xsd во время сборки.
Файлы xsd берутся с сайта гос-учреждения, корректировать невозможно.
Здравствуйте, rus blood, Вы писали:
RB>Почему нельзя вынести одинаковые поля в общий класс?
Это называется duck typing. И во многих ЯП нет такого принципиально. Т.к. это создаёт неявную связь между типами.
Можно размотать через лямбды:
<T> void printValue(Supplier<String> value) {
System.out.println(value.get());
}
...
var a = new A();
printValue(a::value);
var b = new B();
printValue(b::value);
По классике ООП можно делать адаптеры (удобно в случае если методов больше одного):
interface ValueAdapter
{
String value();
static ValueAdapter wrap(A a) {return () -> a.value;}
static ValueAdapter wrap(B b) {return () -> b.value;}
}
void printValue(ValueAdapter adapter) {
System.out.println(adapter.value());
}
var a = ValueAdapter.wrap(new A());
printValue(a);
var b = ValueAdapter.wrap(new B());
printValue(b);
Можно через рефлексию. Но лучше не нужно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, rus blood, Вы писали:
RB>Классы авто-генерируются по xsd во время сборки. RB>Файлы xsd берутся с сайта гос-учреждения, корректировать невозможно.
Ах да, можно ещё подпилить кодогенератор, чтобы проставить нужные интерфейсы. Например https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Inheritance-Plugin
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, rus blood, Вы писали:
RB>Почему нельзя вынести одинаковые поля в общий класс?
С точки зрения вашей генерилки на основе XSD-схемы они все-таки разные. Чем обусловлено ваше стремление "подружить" разные классы? Предположу что находятся generated-sources?
RB>Классы авто-генерируются по xsd во время сборки. RB>Файлы xsd берутся с сайта гос-учреждения, корректировать невозможно.
Их можно локально добавить в resources. Чтобы обеспечить офлайн сборку без прямого соединения (из CI/CD например).
В Kotlin вы можете использовать интерфейсы и обобщенные функции, чтобы достичь подобного поведения, как в вашем примере на C++ с шаблонными функциями. Вам не нужно выносить общие поля в общий класс, и вы можете работать с классами, имеющими общие поля, используя интерфейсы и обобщенные функции.
Следует помнить, что вы будете работать со своими объектами DTO (а не с экземплярами XSD-модели). Таким образом вы сможете дополнительно контролировать, если вдруг XSD-модель внезапмно изменится. В этом случае сборка просто не соберется (потому что мэппинг XSD-in-DTO не найдется) — и это будет сигнал об ошибке.
Здравствуйте, ·, Вы писали:
·>Можно размотать через лямбды: ·>По классике ООП можно делать адаптеры (удобно в случае если методов больше одного):
Классов 5 штук, в каждом примерно 10 одинаковых полей.
Много бойлерплейт-кода.
Фактически это не лучше, чем иметь 5 одинаковых функций для каждого класса.
·>Можно через рефлексию. Но лучше не нужно.
Я пробовал, но это бессмысленное решение.
Фактически на вход поступает xml, он парсится в объектную модель авто-сгенерированных классов.
А потом мы читает поля по именам через рефлексию.
Нафига тогда эти классы объектной модели?
Можно было сразу прочитать нужные поля через x-path-ы из xml.
Здравствуйте, r0nd, Вы писали:
R>С точки зрения вашей генерилки на основе XSD-схемы они все-таки разные.
Верно.
Логически это классы разных документов.
Но у них у всех есть одинаковый набор атрибутов.
Причем, в xsd есть общий базовый тип для них, и часть атрибутов объявлено в базовом типе.
А честь — нет, и хз почему...
R>Чем обусловлено ваше стремление "подружить" разные классы?
Весь этот зоопарк нужно сконвертировать в один класс dto.
Соответственно, с каждым классом чтение одних и тех же полей, одинаковым образом.
При этом, есть еще фишка.
Каждый квартал гос.учреждение выпускает обновление этих xsd.
Фактически, это новая версия и новый набор классов, каждые 3 месяца.
И как минимум одну предыдущую версию нужно поддерживать.
R>Предположу что находятся generated-sources?
Ээээ, наверно.
RB>>Классы авто-генерируются по xsd во время сборки. RB>>Файлы xsd берутся с сайта гос-учреждения, корректировать невозможно.
R>Их можно локально добавить в resources. Чтобы обеспечить офлайн сборку без прямого соединения (из CI/CD например).
Ну, файлы xsd и выложены в resources.
CI/CD с сайта их не берет.
R>В Kotlin вы можете использовать интерфейсы и обобщенные функции, чтобы достичь подобного поведения, как в вашем примере на C++ с шаблонными функциями. Вам не нужно выносить общие поля в общий класс, и вы можете работать с классами, имеющими общие поля, используя интерфейсы и обобщенные функции.
Звучит, как то, что нужно.
Можете показать пример?
R>Следует помнить, что вы будете работать со своими объектами DTO (а не с экземплярами XSD-модели). Таким образом вы сможете дополнительно контролировать, если вдруг XSD-модель внезапмно изменится. В этом случае сборка просто не соберется (потому что мэппинг XSD-in-DTO не найдется) — и это будет сигнал об ошибке.
Ну, суть функционала в том и состоит, чтобы сконвертировать входное xml-сообщение во внутреннее представление (dto), и не зависеть от формата входных данных.
// Объявляем интерфейс с обобщенными функциямиinterface Printable {
val value: Int
fun printValue()
}
// Реализуем интерфейс в ваших классах
data class A(override val value: Int) : Printable {
override fun printValue() {
println(value)
}
}
data class B(override val value: Int) : Printable {
override fun printValue() {
println(value)
}
}
fun main() {
val a = A(42)
val b = B(123)
a.printValue() // Выведет значение из A
b.printValue() // Выведет значение из B
}
В этом примере мы создали интерфейс Printable, который содержит общие поля и функцию printValue(). Затем классы A и B реализуют этот интерфейс, и мы можем вызывать функцию printValue() на объектах обоих классов. Каждый из них будет работать с полем value внутри своего класса.
Такой подход позволяет вам обобщить операции, выполняемые над общими полями в ваших классах, сохраняя при этом индивидуальные особенности каждого класса.
Здравствуйте, r0nd, Вы писали:
r> В этом примере мы создали интерфейс Printable, который содержит общие поля и функцию printValue(). Затем классы A и B реализуют этот интерфейс,
Ты вопрос не понял. Перечитай начальное сообщение. У него проблема была в том, что A и B — генерённые классы и он их не может менять.
Впрочем, похоже ему подошел вариант поменять кодогенератор.
Здравствуйте, ·, Вы писали:
·>Ты вопрос не понял. Перечитай начальное сообщение. У него проблема была в том, что A и B — генерённые классы и он их не может менять.
Не выдумывай отсебятину Толик, вопрос был про шаблонный код в котлах, я отвечал автору принимая во внимания уже существующие ответы (в том числе и два твоих).
·>Впрочем, похоже ему подошел вариант поменять кодогенератор.
Ну и отлично если ему подошел допотопный метод на основе JAXB. Супер.
Здравствуйте, r0nd, Вы писали:
r> ·>Ты вопрос не понял. Перечитай начальное сообщение. У него проблема была в том, что A и B — генерённые классы и он их не может менять. r> Не выдумывай отсебятину Толик, вопрос был про шаблонный код в котлах, я отвечал автору принимая во внимания уже существующие ответы (в том числе и два твоих).
Может быть. Неважно. Просто решение с базовым интерфейсом-классом уж очень очевидное, что вряд ли такое будут спрашивать на форумах.
r> ·>Впрочем, похоже ему подошел вариант поменять кодогенератор. r> Ну и отлично если ему подошел допотопный метод на основе JAXB. Супер.
Любопытно. А какой есть сейчас современный способ работы с гигантскими моделями? Помню искал не так давно для fpml, ничего более разумного не нашел.
Здравствуйте, rus blood, Вы писали:
RB>Коллеги, всем привет!
RB>Пишу на котлин, и возникла такая ситуация. RB>Есть набор разных классов (можно считать, data-классов), одинаковых по структуре, т.е. у них часть полей одинаковы по названиям и типам. RB>Нужно с объектами этих классов делать однотипные действия с "общими" полями. RB>На плюсах я бы сделал это шаблонными функциями. RB>Есть ли аналог шаблонных функций в котлине?
В базовой Java есть шаблонные функции:
import java.lang.reflect.Field;
public class Program {
static <T> void printValue(T t) throws Exception {
final Field field = t.getClass().getDeclaredField("value");
final String value = (String)field.get(t);
System.out.println(value);
}
static class A {
public String value;
}
static class B {
public String value;
}
public static void main(String[] args) {
final A a = new A();
a.value = "A";
final B b = new B();
b.value = "B";
try {
printValue(a);
printValue(b);
} catch (Exception ex) {
System.err.println(ex.getMessage());
}
}
}
Но архитектурно правильно, как уже сказали, создавать специальный класс-адаптер.
В Scala для шаблонных функций есть trait'ы, имеются ли они в Kotlin, не знаю.
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова