Как переопределить метод toMap из класса extends

#dart

Вопрос:

У меня есть класс B, расширяющий класс A, и у него метод toJSON переопределяет метод toJSON класса A. Но когда я переношу класс B в класс A, метод toJSON не будет изменен с класса B на класс A.

 class A {
  String str1;

  A(this.str1);

  Map<String, dynamic> toMap() => {
    'str1': str1,
  }
}

class B extends A {
  String str2;

  B(this.str2, str1) : super(str1);

  Map<String, dynamic> toMap() => {
        'str2': str2,
        'str1': str1,
  }
}


void test(A a) {
  print(a.toMap());
}

// When I pass class B in class A parameter, it still use the class B.toMap method.
B b = B('str2', 'str1');
test(b);
 

Ответ №1:

Все методы экземпляра Dart являются виртуальными, также известными как динамическая отправка. Это означает , что при вызове a.toMap() , какая toMap реализация вызывается, зависит от типа среды выполнения объекта a , а не от статического типа выражения a .

Итак, в данном случае вы вызываете toMap экземпляр B , поэтому вы используете B «s toMap «.

Это довольно распространенное поведение в объектно-ориентированных языках (динамическая отправка-одна из отличительных особенностей объектной ориентации: объект определяет, как реагировать на отправленное вами сообщение, здесь запрос на выполнение операции «toMap».)

Некоторые объектно-ориентированные языки также имеют невиртуальные методы. Дарт этого не делает. Вы можете либо определить статический метод:

 class A {
  static Map<String, dynamic> toMap(A value) => ...;
}
class B {
  static Map<String, dynamic> toMap(B value) => ...;
}
 

а затем выберите, кому из них позвонить:

   print(A.toMap(a));
 

или вы можете использовать методы расширения (которые на самом деле являются просто статическими методами с более приятным синтаксисом вызова), потому что они отправляют на основе статического типа получателя:

 extension AToMap on A {
  Map<String, dynamic> toMap() => ...;
}
extension BToMap on B {
  Map<String, dynamic> toMap() => ...;
}
 

и называйте это нормально:

   print(a.toMap()); // Chooses AToMap.toMap based on static type of `a`.
 

В большинстве ситуаций, если вы действительно хотите получить доступ и A.toMap к тому , и B.toMap к другому экземпляру B , вам лучше переименовать один из методов, чтобы избежать конфликта имен.