根據this article,在Dart中,可能會定義一個非抽象類來具有抽象(或未實現)的方法。 抽象方法會導致警告,但不會阻止實例化。允許在非抽象類中聲明抽象方法的目的是什麼?
允許在Dart的非抽象(或具體)類中聲明抽象方法的目的是什麼? Dart爲什麼設計這樣工作?
根據this article,在Dart中,可能會定義一個非抽象類來具有抽象(或未實現)的方法。 抽象方法會導致警告,但不會阻止實例化。允許在非抽象類中聲明抽象方法的目的是什麼?
允許在Dart的非抽象(或具體)類中聲明抽象方法的目的是什麼? Dart爲什麼設計這樣工作?
具體類中的抽象方法允許您爲通過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();
}
如果我們替換a.f()
與(比方說)a.f(0)
,那麼這將導致錯誤(在強模式)爲被稱爲具有錯誤號碼的參數的方法。如果我們省略void f()
聲明,那麼我們會得到一個錯誤,A
沒有方法f()
。如果我們省略了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]);
}
本示例爲List<T>
創建調試裝飾,顯示所有方法調用。我們使用implements List<T>
來拉入整個列表界面,繼承幾十種抽象方法。當通過dartanalyzer
運行時,這通常會導致警告(或處於強模式,錯誤),因爲我們缺少通常由List<T>
提供的所有這些方法的實現。提供一個noSuchMethod()
實現可以消除這些警告/錯誤。
雖然我們也可以手動包裝所有50多種方法,但這會造成很多打字。如果在我們不必更改代碼的情況下將新方法添加到列表界面,上述方法也將繼續工作。
明確列出具體類中的方法的用例不太常見,但也可能發生。一個例子是將getter或setter添加到這樣一個調試裝飾器,它允許我們檢查或設置委託的實例變量。無論如何,我們需要將它們添加到界面中,以避免使用它們的警告和錯誤;然後使用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;
}
注意,_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).
的specification實際上是有關聲明非常明確抽象方法在一個具體的類:
這是一個靜態警告如果一個抽象構件m爲聲明或在混凝土類繼承
我們希望警告如果一個聲明一個具體類與抽象成員。
這是一個靜態警告如果具體類有一個抽象構件(聲明或繼承)。
他們沒有爲它任何預期的目的,這就是爲什麼他們發出警告。如果您熟悉Java:它與accessing a static member via an object類似,這也是毫無意義的,並會觸發警告。
至於爲什麼它通過編譯,DART使用的optional type system,這意味着打字的概念應該不會影響語言的語義,而這只是什麼鏢執行:
抽象方法的目的是爲類型檢查和反思等目的提供聲明。
的靜態檢查將報告一些違規的類型規則,但這種違法行爲不中止編譯或預先排除執行。
這聽起來像是濫用/濫用'noSuchMethod' - 你爲什麼要把'f'的實現放在'noSuchMethod'中,而不是簡單地給'f'實現本身? –
作爲一個簡單的例子,考慮抽象方法沒有明確列出,但來自'implements'或'extends'子句,'noSuchMethod()'委託給繼承接口的實際實現的情況。 –
你說的是,如果超類型強制子類型來實現行爲,'noSuchMethod'可以委託給超類型的實現。這聽起來不適合你?即使'SuperA'強制執行'Sub'方法,'SuperB'執行執行,也不需要'noSuchMethod'。根據規範:「*我們希望警告是否用抽象成員聲明瞭具體的類,但是像下面這樣的代碼應該沒有警告*」,後面跟着一個代表示例。問題在於明確地*在具體類上聲明抽象方法。 –