I have a class with a nullable property. I would like to make a superclass that overrides that property with a non nullable one
so
class Example {
String? name;
}
class NamedExample extends Example {
#override
String name;
}
Is there some way to do that? if not how is this goal conventionally accomplished.
I basically want two identical classes except one of them always has a property while it is optional in another.
This is a place for the covariant keyword. Normally it does not make sense to override a parameter's type with its subtype and it is invalid to do so. This keyword tells the analyzer this is intentional. It can be added in either the super or subclass.
Subclass:
class Example {
String? name;
}
class NamedExample extends Example {
#override
covariant String name;
NamedExample(this.name);
}
Superclass:
class Example {
covariant String? name;
}
class NamedExample extends Example {
#override
String name;
NamedExample(this.name);
}
The reason why you can't override the String? name member with String name is because it can violate the contract of the setter in the base class and therefore could be unsafe. The base class advertises that:
var example = Example();
example.name = null;
is legal. However, if example instead is an instance of NamedExample, the example.name = null assignment would no longer be legal. The covariant keyword disables this safety check and trusts that you will never perform such an assignment in practice.
In general, you should avoid overriding fields.
You could safely have the override if your classes expose only a getter. Both of the following examples would be legal and safe:
class Example {
String? _optionalName;
String? get name => _optionalName;
}
class NamedExample extends Example {
NamedExample(this._requiredName);
String _requiredName;
#override
String get name => _requiredName;
}
or
class Example {
Example([this.name]);
final String? name;
}
class NamedExample extends Example {
NamedExample(this.name);
#override
final String name;
}
Related
I'm trying to have a base Freezed interface which my app entity interfaces can extend so I can call the freezed functions on the interfaces. I've started the process here which seems to be working so far:
abstract class IUserRegistrationEntity<T> extends FreezedClass<T> {
String get nickName;
String get email;
String get confirmEmail;
String get password;
String get confirmPassword;
}
abstract class FreezedClass<T> {
T get copyWith;
Map<String, dynamic> toJson();
}
freezed class:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:vepo/domain/user_registration/i_user_registration_entity.dart';
part 'user_registration_entity.freezed.dart';
part 'user_registration_entity.g.dart';
#freezed
abstract class UserRegistrationEntity with _$UserRegistrationEntity {
#Implements.fromString(
'IUserRegistrationEntity<\$UserRegistrationEntityCopyWith<IUserRegistrationEntity>>')
const factory UserRegistrationEntity(
{String nickName,
String email,
String confirmEmail,
String password,
String confirmPassword}) = _IUserRegistrationEntity;
factory UserRegistrationEntity.fromJson(Map<String, dynamic> json) =>
_$UserRegistrationEntityFromJson(json);
}
But now I need to add the fromJson factory constructor to the interface. I think this may be what I'm looking for although I can't really tell how to implement it in my code:
T deserialize<T extends JsonSerializable>(
String json,
T factory(Map<String, dynamic> data),
) {
return factory(jsonDecode(json) as Map<String, dynamic>);
}
You an then call it with:
var myValue = deserialize(jsonString, (x) => MyClass.fromJson(x));
Any help adding the fromJson to my freezed interface would be appreciated.
I've found a way to get the same benefits of programming to an interface or "abstraction" with freezed objects, while still getting to call those freezed functions:
#freezed
abstract class Person with _$Person {
#With(BasicPersonMixin)
const factory Person.basicPerson(
{int? id, String? firstName, String? lastName}) = BasicPerson;
#With(FancyPersonMixin)
const factory Person.fancyPerson({String? firstName, required String extraPropMiddleName, String? lastName}) =
FancyPerson;
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
const Person._();
void functionThatEveryPersonShares() {
print('I am a person');
}
String greet() {
return 'override me with a mixin or abstract class';
}
}
mixin FancyPersonMixin {
String get extraPropMiddleName {
return 'my default middle name is John`;
}
String greet() {
return 'Salutations!';
}
void specialisedFunctionThatOnlyIHave() {
print('My middle name is $extraPropMiddleName');
}
}
mixin BasicPersonMixin {
String greet() {
return 'Hi.';
}
}
Now we have 2 concrete classes: BasicPerson, and FancyPerson which are both a Person. Now we can program to Person throughout the app, and still call .copyWith and .fromJson and so on and so forth. The different types of Person can vary independently from each other by using mixins and still be used as a Person type. Works with generics etc (from docs - #With.fromString('AdministrativeArea<House>')) but I have kept the example simple for this question to most simply show the benefits. You can also make Person extend another base class.
I've found another way to let you be a bit more abstract than my other answer. Say you're in a highly abstract super-class, so you don't want to work with objects as specific as Person. You want to work with "a base freezed object"; just cast your type to dynamic in brackets and go ahead and use copyWith freely. Sure, it's not typesafe, but it's a worthy option if it allows you to do something in a super-class rather than in every sub-class.
mixin LocalSaveMixin<TEntity extends LocalSaveMixin<TEntity>> on Entity {
LocalRepository<TEntity> get $repository;
Ref? get provider;
TEntity $localFetch() {
return ($repository.$localFetch() as dynamic).copyWith(provider: provider)
as TEntity;
}
TEntity $localSave() {
return $repository.$localSave(entity: this as TEntity);
}
}
How can I abstract that a methods has optional parameters?
abstract class CopyWith<T>{
T copyWith({}); // Error : Expected an identifier.
}
If I add an identifier like {test} it works and subclasses can have additional arguments
What I want to achieve?
I have a complex state manager, I make some abstraction , the following code is a minimal code, show my problem
import 'dart:collection';
abstract class CopyWith<T> {
T copyWith(OPTIONAL_NAMED_ARGUMENTS);
}
abstract class Manager<K, V extends CopyWith> {
final _map = HashMap<K, V>();
add(K key,V value){
_map[key] = value;
}
void copyWith(K key,OPTIONAL_NAMED_ARGUMENTS) {
assert(key != null);
if (_map.containsKey(key)) {
_map[key].copyWith(OPTIONAL_NAMED_ARGUMENTS);
}
}
}
class User implements CopyWith {
final int id;
final String name;
User({this.id, this.name});
User copyWith({int id, String name}) {
return User(
id: id ?? this.id,
name: name ?? this.name,
);
}
}
class UserManager extends Manager<int, User> {}
void main() {
final userManager = UserManager();
userManager.add(1,User(1,'test'));
userManager.copyWith(1,{test:'test2'})
}
As some one who has faced this issue in my library, I would say the only way is to not put a copyWith in your base class.
Why? Because you should only make a function polymorphic when there IS actually a shared calling convention and behavior. In your example, The way that these two classes perform copyWith is just different. It is, and should be, an error to send a name to Manager.copyWith, because Manager does not have a name to begin with. If you encounter a name inside a Manager.copyWith, that means there is some serious error in your code.
Also, if you actually try to invoke copyWith, as a responsible programmer, you will probably check if you are allowed to pass a name, which is,
if (someObj is User) {
someObj.copyWith(key, name: name);
} else if (someObj is Manager) {
throw IllegalStateError('You should not pass a name to a Manager! What am I supposed to do with the name now?');
}
There, you have already done type checking, so no need to make copyWith polymorphic.
However, some common behaviors can be made polymorphic, like updateKey. You can make Keyable as an interface, and Keyable updateKey(Key key) as an abstract method, and delegate to a non-polymorphic copyWith inside each subclasses.
I am trying to create a base class for my models but I am struggling with the error The name 'cls' isn't a type so it can't be used as a type argument.. So, how can I pass the object's constructor to the Hive.box method?
import 'package:hive/hive.dart';
class AppModel {
#HiveField(0)
int id;
#HiveField(1)
DateTime createdAt;
#HiveField(2)
DateTime updatedAt;
save() async {
final Type cls = this.runtimeType;
// The name 'cls' isn't a type so it can't be used as a type argument.
final Box box = await Hive.openBox<cls>(cls.toString());
await box.put(this.id, this);
return this;
}
}
#HiveType(typeId: 0)
class UserModel extends AppModel {
#HiveField(3)
String email;
#HiveField(4)
String displayName;
}
void main() {
final UserModel user = UserModel()
..email = 'user#domain.com'
..displayName = 'john doe';
user.save().then(() {
print('saved');
});
}
Dart does not have a way to refer to the dynamic type of this (a "self type").
The way such things are often handled is to have a self-type as type argument, so:
class AppModel<T extends AppModel> {
save() async {
final Box box = await Hive.openBox<T>(T.toString());
await box.put(this.id, this as T);
return this;
}
...
and then ensure that each subclass tells the superclass what type it is:
class UserModel extends AppModel<UserModel> {
...
}
(or, if you expect to subclass UserModel eventually:
class UserModel<T extends UserModel> extends AppModel<T> {
...
}
so that a subclass can still pass its type through).
You are also talking about constructors, and for that there is no easy solution.
Dart's type parameters are types, not classes. You cannot access static members or constructors from a type variable, and there is also no other way to pass a class around.
The only way you can have something call a constructor that it doesn't refer to statically, is to wrap the constructor call in a function and pass that function.
(I can't see how you need the constructor here).
Simply i have to classes child and parent class i am new in dart language all i need to assign super class properties from child class
this is super class structure
class Trip{
final int id;
final String title;
final double price;
Trip({this.id,this.title,this.price});
}
and this is child class
class FullTrip extends Trip{
final String data;
FullTrip({this.data}) : super(id:id,title:title,price:price);
}
sure this not working at all
the question is : how can i initialize instance from FullTrip and pass variable for FullTrip and Trip(super class)
thanks in advance
You need to repeat the parameters in the subclass.
class FullTrip extends Trip{
final String data;
FullTrip({this.data, int id, String title, double price}) : super(id:id,title:title,price:price);
}
There are discussions about reducing such boilerplate for constructors, but nothing is decided yet as far as I know.
User super-parameters, which were added in Dart 2.17. For example, say this is your class:
class Parent {
Parent({
int? i,
bool b = false,
required String s,
});
}
Old way (boilerplate code)
Earlier you had to do something like this:
class Child extends Parent {
Child({
int? i,
bool b = false,
required String s,
}) : super(
i: i,
b: b,
s: s,
);
}
New way (neat and clean)
But now you can get rid of those boilerplate code.
class Child extends Parent {
Child({
super.i,
super.b = false,
required super.s,
});
}
If you want to re-initialize in a subclass the not private variables of an extended or implemented upper class before compiling, simply use #override. Look at this example
(if you want to try the code, have in mind it supports null safety. If your test doesn't, simply delete the ? signs.)
class Go {
String? name = "No name";
}
class Foo implements Go { //use either implements or extends
#override
String? name = "Foo";
}
class Doo extends Go { //use either implements or extends
#override
String? name = "Doo";
}
Use case example: In the code above, you can see that in our upper class we have this String name variable. Then in the subclasses we can simply override that variable. After that, in main we can now for example do something like iterating through a List<Go>, access what we wanted to and then trigger something, like printing the name:
void main() {
List<Go> foos = [
Go(),
Foo(),
Doo(),
];
for(Go x in foos){
print(x.name);
}
}
Output:
No name
Foo
Doo
This works if you use either the extends or implements keywords.
Is it possible to automatically initialize fields from a parent class in the constructor?
I get the syntax error:
Could not match parameter initializer 'this.name' with any field
class Type {
String name;
}
class Language extends Type {
String id;
Language(this.name) {
While your case is common, at this time the dart language spec specifically says:
Executing an initializing formal this.id causes the field id of the immediately surrounding class to be assigned the value of the corresponding actual parameter.
This essentially tells us that this.variable notation, in the constructor arguments, will only work on variables in the immediate class and not any parent classes. There are a couple of solutions available: The first is to assign it within the constructor's body:
class Type {
String name;
}
class Language extends Type {
String id;
Language(name) {
this.name = name;
}
}
Alternatively, if we can change the parent class to have a constructor which will initialize the variable then we can use the initializer list in the child class:
class Type {
String name;
Type();
Type.withName(this.name);
}
class Language extends Type {
String id;
Language(name) : super.withName(name);
}
This is assuming there's some reason that the default constructor for Type doesn't automatically initialize name so we created the 2nd named constructor instead.