Skip to content

Invoking methods from abstract classes #47893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ebelevics opened this issue Dec 10, 2021 · 3 comments
Closed

Invoking methods from abstract classes #47893

ebelevics opened this issue Dec 10, 2021 · 3 comments

Comments

@ebelevics
Copy link

I'm using method from abstact class
typedef Converters<View, Data> = FutureOr<Data?> Function(View? view);

but after using it I receive for this example
type '(String?) => String?' is not a subtype of type '(dynamic) => dynamic'

why is this happening? For some reason I thought Dart 2.15 would solve this. Otherwise I need to replace typedef with abstract class and then extending with ordinary class which does work, but it makes code much more boilery, for exact same effect.

@mraleph
Copy link
Member

mraleph commented Dec 10, 2021

You need to provide a more complete reproduction of the problem. I'd guess you did not properly instantiate the type somewhere so Converters ends up being (dynamic) => dynamic - but what exactly happened is hard to say without seeing more code.

@mraleph mraleph added the needs-info We need additional information from the issue author (auto-closed after 14 days if no response) label Dec 10, 2021
@ebelevics
Copy link
Author

ebelevics commented Dec 10, 2021

here is small example to produce the problem

typedef Converters<ViewT, DataT> = FutureOr<DataT?> Function(ViewT? view);

abstract class AbstractControl<ViewT, DataT> {
  late ViewT? initValue;
  final Converters<ViewT?, DataT?> convertToData;
  AbstractControl({this.initValue, required this.convertToData});
}

class Control<ViewT, DataT> extends AbstractControl<ViewT, DataT> {
  Control({
    ViewT? initValue,
    required FutureOr<DataT?> Function(ViewT? val) convertToData,
  }) : super(initValue: initValue, convertToData: convertToData);
}

main() {
  final Map<String, AbstractControl> controls = {
    "firstName": Control<String, String>(
      initValue: "abc",
      convertToData: (val) => val,
    ),
    "lastName": Control<String, String>(
      initValue: "xyz",
      convertToData: (val) => "test + $val",
    ),
  };

  controls.forEach((key, control) {
    final data = control.convertToData(control.initValue);
  });
}

while this works:

import 'dart:async';

abstract class AbstractConverter<ViewT, DataT> {
  FutureOr<DataT?> viewToData(ViewT? val);
}

class Converter<V, D> extends AbstractConverter<V, D> {
  final FutureOr<D?> Function(V? val) converter;
  Converter(this.converter);

  @override
  FutureOr<D?> viewToData(V? val) => converter(val);
}

abstract class AbstractControl<ViewT, DataT> {
  late ViewT? initValue;
  final Converter<ViewT?, DataT?> convertToData;
  AbstractControl({this.initValue, required this.convertToData});
}

class Control<ViewT, DataT> extends AbstractControl<ViewT, DataT> {
  Control({
    ViewT? initValue,
    required FutureOr<DataT?> Function(ViewT? val) convertToData,
  }) : super(initValue: initValue, convertToData: Converter(convertToData));
}

main() {
  final Map<String, AbstractControl> controls = {
    "firstName": Control<String, String>(
      initValue: "abc",
      convertToData: (val) => val,
    ),
    "lastName": Control<String, String>(
      initValue: "xyz",
      convertToData: (val) => "test + $val",
    ),
  };

  controls.forEach((key, control) {
    final data = control.convertToData.viewToData(control.initValue);
    print(data);
  });
}

@no-response no-response bot removed the needs-info We need additional information from the issue author (auto-closed after 14 days if no response) label Dec 10, 2021
@mraleph
Copy link
Member

mraleph commented Dec 10, 2021

Yeah this is a known problem. It is reduced to:

class C<T> {
  void Function(T) f;
  C(this.f);
}

void main() {
  C<dynamic> o = C<String>((String v) { });
  o.f("smth");  // throws: type '(String) => Null' is not a subtype of type '(dynamic) => void'
}

This is a corner case in the type-system: generics are covariant, but T is used in the contravariant position in void Function(T) making it unsound unless there is a dynamic check which verifies a match between declared and actual types in runtime (or alternatively there is a dynamic type check at the entry to the closure - which is something we want to avoid).

So

void main() {
  C<dynamic> o = C<String>((String v) { });
  o.f("smth");
}

is compiled as

void main() {
  C<dynamic> o = C<String>((String v) { });
  (o.f as void Function(dynamic))("smth");
}

Which should explain the error.

See dart-lang/language#1137 for the deeper discussion.

@mraleph mraleph closed this as completed Dec 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants