允许在非抽象类中声明抽象方法的目的是什么?

nbr*_*bro 8 oop dart

根据这篇文章,在Dart中,有可能将非抽象类定义为具有抽象(或未实现)的方法.抽象方法会引发警告,但不会阻止实例化.

允许在Dart中的非抽象(或具体)类中声明抽象方法的目的是什么?为什么Dart设计以这种方式工作?

Rei*_*nds 0

具体类中的抽象方法允许您为通过替代实现的方法提供类型签名noSuchMethod()。提供noSuchMethod()实施也将使警告消失。

在强模式下,简单地在具体类中使用抽象方法将导致错误,除非该类也实现了该noSuchMethod()接口。

简而言之,具体类中抽象方法的目的是为noSuchMethod()实现提供类型签名。这可以避免调用未知方法和在强模式下的警告(这是默认的dartdevc,并且将首先是默认的,然后是 Dart 2.0 的强制模式)这些类型签名对于noSuchMethod()甚至可以编译的代码来说是必需的,除非目标是类型dynamic。

例子:

class A {
  void f();
  dynamic noSuchMethod(Invocation inv) => null;
}

void main() {
  var a = new A();
  a.f();
}
Run Code Online (Sandbox Code Playgroud)

如果我们替换a.f()为(例如)a.f(0),那么这将导致错误(在强模式下),因为使用错误数量的参数调用方法。如果我们省略声明,那么我们将得到一个没有方法的void f()错误。如果我们省略实现,那么就会抱怨缺少方法体,即使Af()noSuchMethod()f()A它不是抽象的。

下面的代码提供了一个更实际的示例:

import "dart:mirrors";

class DebugList<T> implements List<T> {
  List<T> _delegate;
  InstanceMirror _mirror;
  DebugList(this._delegate) {
    _mirror = reflect(_delegate);
  }
  dynamic noSuchMethod(Invocation inv) {
    print("entering ${inv.memberName}");
    var result = _mirror.delegate(inv);
    print("leaving  ${inv.memberName}");
    return result;
  }
}

void main() {
  List<int> list = new DebugList<int>([1, 2, 3]);
  int len = list.length;
  for (int i = 0; i < len; i++) print(list[i]);
}
Run Code Online (Sandbox Code Playgroud)

此示例创建一个调试装饰器List<T>,显示所有方法调用。我们用implements List<T>拉入整个列表接口,继承了几十个抽象方法。当运行 时,这通常会导致警告(或在强模式下,错误)dartanalyzer,因为我们缺少通常由 提供的所有这些方法的实现List<T>。提供一个noSuchMethod()实现可以消除这些警告/错误。

虽然我们也可以手动包装所有 50 多个方法,但这会需要大量输入。如果将新方法添加到列表接口中,则上述方法也将继续有效,而无需更改代码。

在具体类中显式列出方法的用例不太常见,但也可能发生。一个例子是向这样的调试装饰器添加 getter 或 setter,它允许我们检查或设置委托的实例变量。无论如何,我们需要将它们添加到界面中,以避免使用它们时出现警告和错误;然后,该noSuchMethod()实现可以使用getField()和来实现它们setField()。这是上一个示例的变体,使用堆栈而不是列表:

// main.dart

import "dart:mirrors";
import "stack.dart";

class DebugStack<T> implements Stack<T> {
  Stack<T> _delegate;
  InstanceMirror _mirror;
  DebugStack(this._delegate) {
    _mirror = reflect(_delegate);
  }

  dynamic _get(Symbol sym) {
    // some magic so that we can retrieve private fields
    var name = MirrorSystem.getName(sym);
    var sym2 = MirrorSystem.getSymbol(name, _mirror.type.owner);
    return _mirror.getField(sym2).reflectee;
  }

  List<T> get _data;

  dynamic noSuchMethod(Invocation inv) {
    dynamic result;
    print("entering ${inv.memberName}");
    if (inv.isGetter)
      result = _get(inv.memberName);
    else
      result = _mirror.delegate(inv);
    print("leaving  ${inv.memberName}");
    return result;
  }
}

void main() {
  var stack = new DebugStack<int>(new Stack<int>.from([1, 2, 3]));
  print(stack._data);
  while (!stack.isEmpty) {
    print(stack.pop());
  }
}

// stack.dart

class Stack<T> {
  List<T> _data = [];
  Stack.empty();
  Stack.from(Iterable<T> src) {
    _data.addAll(src);
  }
  void push(T item) => _data.add(item);
  T pop() => _data.removeLast();
  bool get isEmpty => _data.length == 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,getter 的抽象声明_data对于类型检查至关重要。如果我们要删除它,即使没有强模式,我们也会收到警告,而在强模式下(例如,使用dartdevc或dartanalyzer --strong),它将失败:

$ dartdevc -o main.js main.dart
[error] The getter '_data' isn't defined for the class 'DebugStack<int>' (main.dart, line 36, col 15)

Please fix all errors before compiling (warnings are okay).
Run Code Online (Sandbox Code Playgroud)