Я не могу понять, как заставить абстрактные классы и их подклассы правильно работать с Freezed. В частности, мне нужно определить абстрактный суперкласс с некоторыми геттерами для переопределения; подклассы должны будут переопределить геттеры, используя заморозку для генерации своего шаблонного кода.
Я понимаю, что предлагает документация, но мне нужно больше.
Я создал репозиторий , чтобы показать, что мне нужно, на случай, если вы захотите перейти к делу. Все, что мне нужно, это эти тесты, чтобы пройти и работать как задумано.
Мне нужен чистый, читаемый и удобный способ сделать это.
В репозитории вы найдете небольшой репродукционный футляр:
Base абстрактный класс;A, который расширяет класс Base (я хотел все упростить, но в моем реальном случае использования есть больше подклассов, скажем X, Y, Z и т. д.);A необходимо реализовать с помощью Freezed, с сериализацией/десериализацией JSON;Резюме: Base существует, чтобы написать точки соприкосновения между A, X, Y и Z; как упоминалось выше, Freezed является обязательным для этих подклассов.
Моя цель — использовать композицию.
Пусть класс B имеет Base get myValue геттер с Freezed. Неудивительно, что я хочу взаимодействовать с этим значением, обращаясь к его методу copyWith (или другим, например toJson), но это довольно быстро усложняется (см. проблему 1).
Снова прочитайте тесты на желаемый результат.
Реализация того, что я описал выше, хотя и имеет смысл, непростая задача (насколько я понимаю).
Например, следующее:
abstract class Base {
const Base();
Function copyWith();
Map<String, dynamic> toJson();
String get id;
}
не будет работать, потому что подклассы будут чувствовать двусмысленность в отношении того, какой метод суперкласса использовать (ошибка здесь): должен ли он использовать тот, который сгенерирован @freezed, или абстрактный? Это ошибка времени компиляции.
Я понятия не имею, как правильно написать контракт, используя Freezed.
Сначала build_runner справедливо жалуется, что не может сгенерировать метод fromJson в B, потому что у Base нет метода @JsonSerializable.
Это подразумевало бы реализацию конвертера вручную: я так и сделал, но это быстро устаревает. Это подразумевает переписывание преобразователя для каждого нового созданного подкласса и создание исключения для подкласса, который еще не обрабатывается (это сильный запах кода).
Такое зверство находится в этом файле.
Как можно достичь этого чистым способом?





Ваша проблема, вероятно, связана с Base определением пустого copyWith
Freezed реализует copyWith не как метод, а как геттер, который возвращает функцию (или вызываемый объект). Это необходимо для поддержки copyWith(foo: null), но несовместимо с вашим интерфейсом.
Расширение Base также может быть проблемой. Freezed не очень хорошо поддерживает наследование из-за языковых ограничений. Вам, вероятно, лучше реализовать Base или сделать его миксином.
В общем, вы могли бы сделать:
mixin Base {
int get id;
Map<String, dynamic> toJson();
}
@freezed
class A with _$A, Base {
const A._();
factory A({
required int id;
}) = _A;
factory A.fromJson(Map<String, dynamic> json) => _$AFromJson(json);
@override
Map<String, dynamic> toJson() => _$AToJson(this);
}
Нет, к сожалению, это не работает. Пожалуйста, проверьте мой репозиторий еще раз (я только что выдвинул эти предложения) и убедитесь, что: (1) мне все еще нужно реализовать BaseConverter, что является еще одним запахом, иначе B не будет генерировать свой код; и (2) что-то ломается при генерации класса A, так как a.freezed.dart содержит эту ошибку: The method 'copyWith' isn't defined for the type '<unknown>'. Может ты хотел написать class A with _$A, Base ?
Ну, Base не имеет fromJson, так что вы не можете волшебным образом его расшифровать. Если вам не нужен JsonConverter, не стесняйтесь добавлять fromJson к Base
Кроме того, отсутствие copyWith в качестве геттера на миксине для меня все еще остается открытой проблемой, я не могу от этого избавиться.
Привет еще раз и еще раз спасибо за помощь! Я понимаю, что у Base нет fromJson, моей целью было вызвать его подклассы, но... это невозможно! Это ошибка в моих рассуждениях. B.fromJson не может знать, должен ли он запускать A.fromJson или любой другой X.fromJson этот подкласс Base. Так что пользовательский BaseConverter должен быть реализован, от этого никуда не деться. Еще немного поразмыслив, я пришел к выводу, что эта проблема просто не решается с помощью Freezed и что я должен реализовать все вручную. Я помогу себе с Freezed в других контекстах.
В частности, (1) я не могу решить проблему абстрактного метода copyWith и (2) решение вышеупомянутой проблемы путем введения свойства key портит решение, которое вы нашли (toJson на самом деле не настраивается, потому что конвертер будет использовать этот метод на тип _$_A, см. репозиторий). Если бы в Dart были классы данных, я бы просто создал Base абстрактный класс данных, и все. Грустный.
В любом случае, спасибо за вашу помощь, я не могу отметить ваш ответ как принятый, так как не могу сказать, что мои проблемы точно «решены». Тогда я подожду метапрограммирования.
После двух дней работы и исследований мне удалось выбраться из этой неразберихи.
freezed (я просто оставлю его для системы союзных типов);Я опубликую здесь быструю реализацию псевдокода, достижимую с помощью dart_mappable:
@MappableClass()
abstract class MyBase with MyBaseMappable {
const MyBase(this.id);
final String id;
}
@MappableClass()
class A extends MyBase with AMappable {
const A(super.id);
}
@MappableClass()
class B with BMappable {
const B(this.value);
final MyBase value;
}
void main() {
const a = A('hello');
const b = B(a);
print(b.value.copyWith(id: "lol")); // Yes!
}
Для работы требуется версия 2 версии dart_mappable, которая в настоящее время находится в состоянии предварительного выпуска.
Спасибо, Реми!! В конце концов, все, что мне нужно, — это способ доступа к сгенерированным Freezed методам из общего интерфейса, а также несколько общих методов получения, которые необходимо реализовать в каждом подклассе. Я попробую этот Mixin и посмотрю, смогу ли я сойти с рук.