Flutter-Function 2

Functionというかオーバーロード・オーバーライド関連についてちょっと調べてみた。

  • 下記内容はDart Version 2.10の仕様をもとに記載している。

オーバーロード

定義

Dartには定義がないので、C++の方を参照すると以下のような記述がある。(参照先はここ)

同じスコープ内で同じ名前の複数の関数を指定できます。 これらの関数は、 オーバーロード された関数と呼ばれます。

Dartでは

実際Dartの仕様ではどうかというと、「overload」という単語で検索できる箇所は1か所のみで、以下の様な記載がされている。

それが operator – の定義。仕様「10.1.1Operators」に記載がある。

The ‘-’ operator is unique in that two overloaded versions are permitted.
If the operator has no arguments, it denotes unary minus.If it has an argument, it denotes binary subtraction.

“-“演算子は、2つのオーバーロードされたバージョンが許可されるという点で独特です。
演算子に引数がない場合は、単項マイナスを示します。引数がある場合は、バイナリ減算を示します。

オーバーロードと呼ぶ仕様の定義はないのだけど、一般的に呼ばれるオーバーロードの定義ができるのが-(マイナス)演算子の定義だけというもの。

実際のコードは以下の様になる。

class Hoge {
  Hoge(this.a);

  int a = 0;

  Hoge operator -() {
    return Hoge(-a);
  }

  Hoge operator -(Hoge val) {
    return Hoge(a - val.a);
  }
}

void main(List<String> args) {
  final a = Hoge(10);
  final b = Hoge(3);

  print("-a = ${(-a).a}");
  print("a-b = ${(a - b).a}");
}

オーバーロードを導入しないのかどうかについては、https://github.com/dart-lang/language/issues/1122で討論が続いているようだ。

また使用できないという記述はlanguage-tourに書かれておらず、微妙な表現だけどhttps://dart.dev/guides/language/effective-dart/design#avoid-using-runtime-type-tests-to-fake-overloadingに記述があった。

Dartのオーバーロード代替

Dartでオーバーロード相当を使用したい場合にはどうするか。
それはnamed parameterやoptional position parameterがあり、そちらを使うということになる。
実際はnamed parameterになると思うが。

なおnamed parameterやoptional position parameterは混在は不可である。

これはDartの仕様書の9.2 Formal Parametersに以下の記述がある。

The optional parameters may be specified either as a set of named parameters or as a list of positional parameters, but not both.

オプションのパラメータは、名前付きパラメータのセットまたは定位置パラメータのリストとして指定できますが、両方を指定することはできません。

1次情報以外のWEBの記載について思うところ

「Dart オーバーロード」で記載すると、それなりに間違った記述の掲載がある。

分けてみると2種類に分かれるかも。

  1. オーバーライドとオーバーロードを間違って認識しているもの。
  2. オーバーライドの仕様上の書き方をオーバーロードとして認識しているもの。

後者については、仕様の中の「戻り値の型、引数の型のクラスとして、サブタイプも許容、もしくはスーパータイプも許容する」というのがあり、その結果元の関数で指定されているクラスと異なることから「なんとなく型が異なるからオーバーロードじゃないの」と認識してしまったものと思う。

実際以下の書き方ではHage.mulの2番目に記述した関数がエラーになっている。

class Hoge {
  Hoge(this.a);

  int a = 0;

  Hoge operator -() {
    return Hoge(-a);
  }

  Hoge operator -(Hoge val) {
    return Hoge(a - val.a);
  }

  void mul(Hoge val) {
    a *= val.a;
  }
}

class Hage extends Hoge {
  Hage(super.a);

  @override
  void mul(Object val) {
    a *= (val as Hage).a + 10;
  }

  @override
  void mul(Hoge val) {    // error
    a *= val.a + 9;
  }
}

エラーを取り除いた場合、Hage.mulは引数としてObject型を取り扱うことができるようになる。
(ただこういう記述がeffective-dart内の避けるべき記述に該当するのだと思う)

親のHogeではHogeを受け取れるので、なんとなくHage.mulとHoge.mulの2つの関数があるから、Hage側は引数で与えられた型によって型認識してくれるかなと思うようだけどそんなことはなくHage.mulしか呼び出されない。

オーバーライド

Dartの定義

これは仕様の11.2.2 Correct Member Overridesが該当するのだと思う。

関数というくくりではなく、クラスメンバー(属性・関数)に同一の名称があった場合の取り扱いに関しての仕様といったくくりになるのだろうと思う。

どういうものをオーバーライドとしてとりあつかについては、上の仕様に記述があるが、A tour of the Dart languageのlanguage-tour→Extending a class→Overriding membersに書かれているものを見たほうがわかりやすいのかもしれない。

コメント

タイトルとURLをコピーしました