Open6

[Dart] Positional parameters vs Named parameters

にしにし

Parameters と Argumentsの違い

日本語における引数と混同することがあるので違いをメモ。

Parameters (パラメータ)

関数やメソッドを定義する際に指定される変数。

void greet(String word){
    print(word);
}

この word が Parametersと呼ばれ、入力を受け取るための変数としての役割がある。

Arguments(引数)

関数やメソッドを実際に呼び出す際に指定される値。

void main(){
    greet('Hello!');
}

この Hello! がArgumentsと呼ばれ、関数やメソッドが呼ばれる際にParametersに代入される。

にしにし

Positional parameters

日本語では 位置パラメータ と呼ばれることが多い。
Parameters の指定方法は 順序(位置)を基づいて割り当てられるという特徴

void main(){
    greet('Taro', 'Tanaka');
    // output Hello Taro Tanaka. 
}
	
void greet(String firstName, String lastName) {
    print('Hello $firstName $lastName.');
}

Argumentsを渡す際に位置が重要。

初期値やオプションとして指定する場合

初期値を指定する方法

該当のParametersを[]で囲み、[String lastName = 'Tanaka']のように、変数を指定。
この場合、呼び出す際にlastNameのArgumentsを指定するのは省略可能。(greet()みたいな)

// OK
void greet([String lastName = 'Tanaka']) {
    print('Hello Taro $lastName.');
}

// NG []を使用する必要がある
void greet(String lastName = 'Tanaka') {
    print('Hello Taro $lastName.');
}

オプションとして指定する方法

該当のParametersを[]で囲み、[String? lastName]のように、TypeをNull許容に指定。
この場合、呼び出す際にlastNameは省略可能であるが、関数内でNullに対応する必要がある。

// OK
void greet([String? lastName]) {
    if(lastName == null){
        print('Hello Taro.');
        return;
    }
    print('Hello Taro $lastName.');
}

// NG default値がない場合はNull許容にする必要がある
void greet([String lastName]){}

Null許容かつ初期値を指定することも可能。

void greet([String? lastName = 'Tanaka'])

Argumentsにnullを指定することも可能で、lastNameを省略した場合は、'Tanaka'が代入される。

にしにし

Named parameters

日本語では 名前付きパラメータ と呼ばれることが多い。
関数やメソッドを呼び出す際に、パラメータ名を明示的に指定してArgumentsを渡す。
変数は{}で囲む必要がある。

void main() {
   greetNamed(firstName: 'Taro', lastName: 'Tanaka');
}
void greetNamed({String? firstName, String? lastName}) {
   print('Hello $firstName $lastName.');
}

void greetNamed({String firstName, String lastName})という様に定義するとエラーになる。
必須Parametersには変数の前にrequiredアノテーションをつける必要がある。
例:

void greetNamed({required String firstName, required String lastName})

初期値やオプションとして指定する場合

初期値を指定する方法

Positional parametersと大きな違いはない。

void greetNamed({String lastName = 'Tanaka'}) {
    print('Hello Taro $lastName.');
}

オプションとして指定する方法

こちらもPositional parametersと大きな違いはない。

void greetNamed({String? lastName}) {
    if(lastName == null){
        print('Hello Taro.');
        return;
    }
    print('Hello Taro $lastName.');
}

Null許容かつ初期値を指定することも可能。

void greetNamed({String? lastName = 'Tanaka'})
にしにし

PositionalとNamedの組み合わせ

Parametersの定義方法を組み合わせて使用ができる。
注意点として、Positional parametersを先に定義する必要があある。

void greetNamedAndPositional(String lastName, {String? firstName}) {
    print('Hello $firstName $lastName.');
}

だたし、呼び出す際のArgumentsはPositionalとNamedどちらかでも指定できる。
↓どちらでも動作する。

greetNamedAndPositional(firstName: 'Taro', 'Tanaka');
greetNamedAndPositional('Tanaka', firstName: 'Taro');
にしにし

どちらを使用するべきか

個人的にはNamed parametersを使用する方が良い。
理由:
->順序を気にする必要がない。
->明示的にParametersを指定する必要があるので、間違った変数を渡すリスクが減る。

ただし、一部の関数やメソッドにはPositional parametersを使用しても良いと考える。

  1. パラメータが1つ
  2. 順序が関係しない

Named と Positionalの併用

Flutterの標準ClassではNamed と Positionalを併用しているものもある。
https://api.flutter.dev/flutter/widgets/Text-class.html
https://api.flutter.dev/flutter/widgets/Icon-class.html

このことから、Classに対して明らかに必要なパラメータがある場合(Textの文字列やIconのIconDataなど)はわざわざNamedを使用する必要はないのかもしれない。