Method overriding rules in Dart - dart

Please look at the code below:
void main() {
Child c = new Child();
c.m1("12");
}
class Parent {
void m1(int a) {
print("value of a $a");
}
}
class Child extends Parent {
#override
void m1(String b) {
print("value of b $b");
}
}
When I run the program, it shows me the following error message:
Error: The parameter 'b' of the method 'Child.m1' has type 'String', which does not match the corresponding type, 'int', in the overridden method, 'Parent.m1'. Change to a supertype of 'int', or, for a covariant parameter, a subtype.
I didn't quite understand this error message. What are the conditions of overriding a method in dart?
Probably I cannot change the data type while overriding the function. Is it?

Related

Adding a void callback with a generic parameter to a function in Dart

What I'm trying to do
Given the following Node:
class Node<T> {
Node(this.value);
T value;
Node? child;
// TODO: add `visit` method
#override
String toString() => value.toString();
}
I'd like to add a visit method that will perform some action on the value of each node and its child recursively. Then I could do something like this:
void main() {
final root = Node(1);
root.child = Node(2);
root.child!.child = Node(3);
// one of these
root.visit(print);
root.visit((value) => print(value));
// 1
// 2
// 3
}
Naive solution
If I do the following, it works:
void visit(Function action) {
action(value);
child?.visit(action);
}
Problems with the naive solution
However, the value in this statement is inferred to be dynamic:
root.visit((value) => print(value));
I'd like to infer it to be the same type as the Node's generic T type.
Additionally, the compiler allows the following, which causes a runtime crash:
root.visit(() => 42);
I'd like that to be a compile-time error.
Attempted solution 1
If I change visit to the following:
void visit(Function(T value) action) {
action(value);
child?.visit(action(value));
}
Everything looks good at compiletime:
root.visit(print); // OK
root.visit((value) => print(value)); // OK
root.visit(() => 42); // error
But if I comment out that last one and run the code on either of the first two then I'll get the following runtime error:
Unhandled exception:
type 'Null' is not a subtype of type '(dynamic) => dynamic'
I'm not exactly sure what that means.
Attempted solution 2
Added void:
void visit(void Function(T value) action) {
action(value);
child?.visit(action(value)); // error
}
This expression has a type of 'void' so its value can't be used.
Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void. (dartuse_of_void_result)
Attempted solution 3
This one was just a stab in the dark:
void visit(void Function<T>(T value) action) {
action(value);
child?.visit(action);
}
The visit method seems to compile but calling it as before gives compile time errors:
root.visit(print); // error
root.visit((value) => print(value)); // error
The errors read:
The argument type 'void Function(Object?)' can't be assigned to the parameter type 'void Function(T)'. (dartargument_type_not_assignable)
Related questions
These questions seem related but I couldn't figure out how to extract a solution from them:
How to create a generic method in Dart?
Callback with generic type parameter in Dart
Dart: Type error when overrding generic function in abstract class
How to check and cast generic parameters in Dart?
Dart passing generic Function<T>(T t) seems to require cast, all other ways signatures don't match
How can I solve the problem?
Thank you to #jamesdlin in the comments for solving the problem.
You need to set the generic type for the child as Node<T>. Then you can specify the method signature as void visit(Function(T value) action) and pass action itself on to the child.
Here is the full example:
void main() {
final root = Node(1);
root.child = Node(2);
root.child!.child = Node(3);
// one of these
root.visit(print);
root.visit((value) => print(value)); // value inferred as int
// root.visit(() => 42); // compile-time error
// 1
// 2
// 3
}
class Node<T> {
Node(this.value);
T value;
Node<T>? child;
void visit(Function(T value) action) {
action(value);
child?.visit(action);
}
#override
String toString() => value.toString();
}

Restricting generic type in extensions in Dart

I'm trying to add an extension method on lists with items extending a specific class. I thought this would work:
abstract class Foo {
bool bar();
}
extension FooListExtension on List<T extends Foo> {
List<T> bar() {
return where((e) => e.bar()).toList();
}
}
But I'm getting the following error from analyzer:
The name 'T' isn't a type so it can't be used as a type argument
I was wondering if there is any way to achieve it?
The correct syntax is the following since you need to apply the generic requirements on the extension itself. You can then use the generic (e.g. T) in your extension:
abstract class Foo {
bool bar();
}
extension FooListExtension<T extends Foo> on List<T> {
List<T> bar() {
return where((e) => e.bar()).toList();
}
}

void Function(int) isn't a valid override of void Function(dynamic)

class Parent<T> {
void method(T t) {}
}
class Child extends Parent {
#override
void method(int i) {} // error: mentioned_below
void takesDynamic(dynamic d) {
takesType(d); // no error
}
void takesType(int i) {
takesDynamic(i); // no error
}
}
Error:
void Function(int) isn't a valid override of void Function(dynamic)
When I can easily pass int to dynamic and vice-versa in a method parameter, why do I see the error when I override method.
PS:
I am not looking for a solution which is to use extends Parent<int> and get it working, I want to know the reason why things are treated differently when I am overriding a method vs calling regular methods.
void Function(int x) normally isn't a valid override of void Function(dynamic x) because the int version is not substitutable for the dynamic version.
What are the allowed inputs to Parent<dynamic>.method? Anything.
What are the allowed inputs to Child.method? Just ints.
Such an override therefore could violate the contract of Parent<dynamic>'s interface. (For example, what if you had an instance of Child and passed it to something that expected Parent<dynamic>, which then invoked method('not an int') on it?)
(Note that this is not specific to method overrides. In general, a function that takes a narrower type cannot be used where a function that takes a wider type is expected, even if the narrower type derives from the wider type.)
Dart does allow you to use the covariant keyword to suppress the static type error and explicitly allow the override, but be aware that doing so isn't necessarily type-safe, and you would be responsible for ensuring that you don't get type errors at runtime.
Further reading: Covariance and contravariance (computer science) from Wikipedia

How to override a method in child class without optional parameter?

I have some class like this:
abstract class Parent {
void doSomething({dynamic number});
}
class Child01 extends Parent {
#override
void doSomething({dynamic number}) {
...
}
}
class Child02 extends Parent {
#override
void doSomething() {
...
}
}
How can I override doSomething() method in class Child02 without an optional parameter, like above?
You cannot do that and the reason can be seen in the following code:
List<Parent> list = [Child01(), Child02()];
list.forEach((e) => e.doSomething(number: 5));
This is allowed since Parent tells us there is an optional parameter to doSomething called number. But what happens when we end up calling a Child02 object with this parameter it does not expect? You are not allowed in Dart to send arbitrary arguments (named or not) to a method and see if it is going to be used or not.

How to capture the generic type of a generic constraint?

Consider the following generic class:
class Foo<T extends Bar<dynamic>> {}
How can I obtain the generic argument of Bar at compile time?
I would expect the following code to print int, but it prints dynamic:
class Bar<T> {}
class Foo<T extends Bar<dynamic>> {
Foo(this.value) {
_debugBarType(value);
}
final T value;
void _debugBarType<A>(Bar<A> value) {
print(A);
}
}
void main() {
Foo<Bar<int>>(Bar());
}
I know I could do:
class Foo<A, Bar<A>> {}
But I would like to define the Foo class using a single generic parameter instead of two.
This is currently not possible to do on any class.
There's currently a pending feature request on Dart to support such use-case: https://github.com/dart-lang/language/issues/620
It's worth noting that there's a workaround if you can add a method on the class concerned.
For example, say you have a generic class Model<T>:
class Model<T> {
T value;
}
Then you can add a "capture" method as such:
class Model<T> {
T value;
R capture<T>(void cb<P>(P value)) => cb<T>(value);
}
This then allows you to write:
void someFunction<T extends Model<dynamic>>(T model) {
model.capture(<P>(value) {
// `P` will be the captured type instead of `dynamic`
});
}

Resources