本稿、あるいは本シリーズはFlutterの公式ドキュメントを参考に初学者が理解を深めるものである。
superってなんだ。なぜ使うんだろう、という疑問が今も浮かびながら本稿を執筆している。例えば、FlutterのデモコードではStatefulWidgetを継承したMyHomePageに子クラスのコンストラクタの他に、super(key: key)なるものが記述されている。
初学者としてはこのsuperがなんなのか理解を深めなければ、コードが何を指すのか分からない場面が多々出てくる。
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
...
以下略
MyHomePageはStatefulWidgetを継承している。ということはMyHomePageクラスはStateful Widgetの子クラスで、StatefulWidgetはMyHomePageクラスの親クラスということになる。そして、superは親クラスのコンストラクタを呼び出すことができるものである。
上記のコードでは、MyHomePageクラスのコンストラクタにはkeyというパラメータがある。ここで受け取ったkeyを親クラス(StatefulWidget)に受け渡しているのだ。しかし、実際には上記コードではkeyはnullであるという(正直Keyの使い方が分からなくて、困っているがその解説は本稿の趣旨から外れるため後日調べる)。とにかく、本稿ではsuperは親クラスのコンストラクに引数を受け渡していると説明する。
では、なぜsuperが必要なのか。
例えば、『タクティクスオウガ』の主人公デニムは他のキャラと同様、クラスチェンジができる。デフォルトのソルジャーから、ナイトにクラスチェンジした際、ステータスに変化が生じるが、そのステータスの増減はデニムの元のステータスを参照しなければならない。
つまり、他のキャラがナイトになるのと、デニムがナイトになるのではそれぞれステータスが違って当然だ。今回はそのデニムのステータスをsuperを使って表現してみた。
以下のコードは一応、現在の理解力で記述したもので、2021年9月11日現在、動作を確認している。しかし、もっと良い書き方があるかもしれない。
void main() {
Knight knight = Knight(speed: -10, power: 20, vital: 6);
knight.knightStatus();
knight.denimWords();
}
//親クラスの定義
class Denim {
int denimSpeed = 3; //デニムの元々のスピード
int classSpeed; //デニムのクラスによるスピード補正
int denimPower = 5; //デニムの元々の力
int classPower; //デニムのクラスによる力補正
int denimVital = 3; //デニムの元々の体力
int classVital; //デニムのクラスによる体力補正
Denim({required this.classSpeed, required this.classPower, required this.classVital}) {}
void denimWords() {
print('馬鹿なことはやめるんだッ!');
}
}
//子クラスの定義
class Knight extends Denim {
//親クラスのコンストラクタを使用
Knight({required int speed, required int power, required int vital}) : super(classSpeed: speed, classPower: power, classVital: vital) {}
void knightStatus() {
print('デニムのスピードは${super.denimSpeed + super.classSpeed}');
print('デニムの力は${super.denimPower + super.classPower}');
print('デニムの体力は${super.denimVital + super.classVital}');
}
@override
void denimWords(){
print('……わかっています。');
}
}
子クラスKnightは親クラスである、Denimを参照しており、自身のコンストラクタで引数として受け取ったspeed等のステータスをsuperを使いDenimクラスのコンストラクタを呼び出している。そして、classSpeed等、各ステータスに渡しているのだ。
そして、knightStatus()では元々のステータスとKnightのステータス増減の合計値を出力させている。
ちなみに、Knightクラス内の @override 以下の denimWords()は親クラスであるDenimのセリフを上書きしている(原作を知っている人からすれば、なんでナイトになったらロールートに行くことになってるのだとツッコミたくなるだろうが)。
これをさらに利用するならば、クラスごとにステータスを変化させることだってできるし、戦闘中のセリフを変えたり、死亡時の処理を変化させることもできる。
親クラスの元々の処理を活かすことで、処理の方向性が散らばらずに済むような気がした(あくまでも最後は親クラスのコンストラクタに引き渡して、インスタンス生成時に処理してもらう)。
参考: https://flutternyumon.com/how-to-use-class-inheritance/