質問: Flutter新規プロジェクトを作成したときに生成されるデフォルトmain.dartのbuildメソッドについて。 ■調査した内容 以下の公式ドキュメントやMedium投稿等を読みましたが、直接的に当質問に対応する箇所が見当たりませんでした。概念は理解したのですが、実体的な理解までは至っておりません。 Widget — State — BuildContext — InheritedWidget(Medium) Widget class Introduction to widgets BuildContext class build method ■現状の理解 画面描画するときにこのbuildメソッドを使うんだな、という程度のグラグラな認識です。 ■質問 1) 冒頭のabstract class "Widget" (だけ?)はなぜ明示的に宣言されているのですか?一応、"Widget"部を削除してもコードが正常動作することは確認しました。 2) build(BuildContext context)は、abstract class BuildContextとcontextがbuildメソッドの引数という構造でしょうか? 3) 上記、各トークンの意味を踏まえ、この@override、Widgetクラス、buildメソッドを平易な表現でご解説いただけると大変ありがたいです。どうぞよろしくお願い申し上げます。 ``` @override Widget build(BuildContext context){ return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } ```
回答: 質問の順序が前後しますが、回答します 2 > > build(BuildContext context)は、abstract class BuildContextとcontextがbuildメソッドの引数という構造でしょうか? > > > * これは`BuildContext`型の引数`context`が`build`メソッドの引数で、引数としては一つです この質問から、質問者さんは静的型付け言語にあまり多く触れられたことがないかと推察いたしますが、 静的型付け言語では基本的に変数などを`<クラス(型)名> <変数名>`と宣言します。 dartは、コード的にはjavascriptのような形で、部分的にクラス(型)名を書かなくても動作するようになっていますが、これは動的型付けだからではなく、型推論のある静的型付け言語だからです。 まず、dartの仕様を読んでみることをお勧めします * https://www.dartlang.org/guides/language/language-tour[Dart Language Tour] 1 > > 冒頭のabstract class "Widget" (だけ?)はなぜ明示的に宣言されているのですか? > > > これにはまず`@override`の説明をします。 記載のある`build`メソッドは、おそらく `class <クラス名> extends StatelessWidget` か `class <クラス名> extends State<クラス名2>` のような宣言をされたクラス内で定義されたメソッドであるはずです。 ここで`extends`はクラスの拡張を示していて、 一般的に、上の例では`StatelessWidget`, 下の例では`State<クラス名2>`を **親クラス** と言います。 それと対応して例で定義されている`<クラス名>`というクラスは **子クラス** と言います。 子クラスは基本的に親クラスに定義されたメソッドを受け継いで持っており、 `@override`が記載されているメソッドというのは、親クラスに定義されたメソッドを上書きしますよ、という宣言です。 `@override`は無くてもメソッド宣言が親クラスにあるものと一致していれば勝手に上書きになるのですが、きちんとつけておくことで何かと便利なのでつけます。 `@override`がついているのにも関わらず、親クラスに同名のメソッドがない時などに、警告を出してくれたりします。 親クラスのメソッドを上書きしたつもりが、誤字で上書きできていなかったケースなどに気づけます。 さて、ここから冒頭の`Widget`の戻り値の型宣言がなぜ明示的にされていて、 かつ消しても動いたかについてですが、 dartではメソッドの戻り値の型宣言を省略すると、通常`dynamic`という特殊な型を戻り値とします。 (ざっくり言うと戻り値の型がなんでも良いという状態です。詳しくはdartの言語仕様を確認してください。) dartでは仕組み上、1つのクラス内に同じ名前のメソッドを複数持つことができません。 (例えばJavaではメソッド名が異なっても、引数の個数や型などが違う複数の同名メソッドを持つことができます) 少し複雑になりましたが整理すると * `extends`キーワードでクラスを拡張すると、子クラスは親クラスと同じメソッドをデフォルトで持っていることになる * dartでは1つのクラス内に同じ名前のメソッドを複数持つことができない という点が重要で、`Widget`の宣言を消したときに起こっているのは * 普通は戻り値の型の指定がない場合は`dynamic`が戻り値の型とされる * だが、今回は親クラスに同名のメソッド(`build`)があるため、子クラスにもデフォルトで引き継がれている * 1つのクラス内に`dynamic`型を返す`build`メソッドと`Widget`型を返す`build`メソッドは同居できないので、親クラスの宣言が有効とされた * したがって、明示的に書いていなくとも、`build`メソッドは`Widget`型を返すものとして解釈され、Flutter全体としてもエラー無く動作した です 3 > > 上記、各トークンの意味を踏まえ、この@override、Widgetクラス、buildメソッドを平易な表現でご解説いただけると大変ありがたいです。 > > > * `@override`は上でも説明した通り、親クラスのメソッドを上書きしますよという宣言です * なくても動きますが、便利なのと、お作法的にきちんと書くことがほとんどです。 * `Widget`クラスは、flutter内では画面に描画するものの基本単位となるクラスです。 * 画面に表示するものは`Widget`及びその子クラスを拡張してクラスとして作成します * `StatelessWidget`, `StatefulWidget`も`Widget`の子クラスです * したがって、`StatelessWidget`を拡張して作ったクラスも間接的に`Widget`の子クラスと言えます * `build`クラスはそのクラスを画面描画するときにFlutterのフレームワークが呼び出してくれるメソッドです。 * ここで描画したいものを`return`することで、画面にパーツが描画されます。 以上、変に複雑になりましたが回答とさせていただきます。