2017-08-17 211 views

回答

0

具體類中的抽象方法允許您爲通過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吸氣的抽象聲明進行類型檢查是至關重要的。如果我們將其刪除,我們會得到一個警告,即使沒有強大的模式,在強大的模式(比如,與dartdevcdartanalyzer --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). 
+0

這聽起來像是濫用/濫用'noSuchMethod' - 你爲什麼要把'f'的實現放在'noSuchMethod'中,而不是簡單地給'f'實現本身? –

+0

作爲一個簡單的例子,考慮抽象方法沒有明確列出,但來自'implements'或'extends'子句,'noSuchMethod()'委託給繼承接口的實際實現的情況。 –

+1

你說的是,如果超類型強制子類型來實現行爲,'noSuchMethod'可以委託給超類型的實現。這聽起來不適合你?即使'SuperA'強制執行'Sub'方法,'SuperB'執行執行,也不需要'noSuchMethod'。根據規範:「*我們希望警告是否用抽象成員聲明瞭具體的類,但是像下面這樣的代碼應該沒有警告*」,後面跟着一個代表示例。問題在於明確地*在具體類上聲明抽象方法。 –

2

specification實際上是有關聲明非常明確抽象方法在一個具體的類:

這是一個靜態警告如果一個抽象構件m爲聲明或在混凝土類繼承


我們希望警告如果一個聲明一個具體類與抽象成員


這是一個靜態警告如果具體類有一個抽象構件聲明或繼承)。

他們沒有爲它任何預期的目的,這就是爲什麼他們發出警告。如果您熟悉Java:它與accessing a static member via an object類似,這也是毫無意義的,並會觸發警告。

至於爲什麼它通過編譯,DART使用的optional type system,這意味着打字的概念應該不會影響語言的語義,而這只是什麼鏢執行:

抽象方法的目的是爲類型檢查和反思等目的提供聲明。


的靜態檢查將報告一些違規的類型規則,但這種違法行爲不中止編譯或預先排除執行。

相關問題