アプリ開発日誌
2021.09.30
Flutter開発のためのDart入門(7)クラス②
前回の投稿に続き、今回もDartについて解説していきます。
引き続きクラスについて解説します。
抽象クラス
抽象クラスとは継承されることを前提として、処理内容を記述しないメソッド(抽象メソッド)を含むクラスです。
抽象クラス単体ではインスタンス化することはできません。
反対にインスタンス化できるクラスは具象クラスと呼びます。
抽象クラスを定義する際はabstract
キーワードを指定します。
abstract class 抽象クラス名 { }
抽象メソッド
抽象クラスには抽象メソッドを定義できます。
抽象メソッドには処理内容を記述できません。
メソッド名()
の後ろの{処理内容}
の代わりにセミコロン;
を記述します。
abstract class Shape { double getArea(); // 抽象メソッド }
継承
既存のクラスを元に新たなクラスを定義することを継承と呼びます。
また、継承において、元となるクラスをスーパークラス、新たに定義されるクラスをサブクラスと呼びます。
サブクラスを定義する際には、extends
キーワードを使用します。
次のコードは先程の抽象クラスを継承した例です。
abstract class Shape { // 抽象クラス double getArea(); // 抽象メソッド } // 抽象クラスを継承 class Rectangle extends Shape { double width = 0; double height = 0; Rectangle(this.width, this.height); // 抽象メソッドを実装 double getArea() { return this.width * this.height; } } void main() { var rect = Rectangle(3, 4); print('四角形の面積は${rect.getArea()}㎠'); }
実行結果
四角形の面積は12㎠
※Dartでは総てがオブジェクトです。Javaのプリミティブ型(intやbooleanなどのデータ型)のようなものは存在しません。すべてのオブジェクトは Objectクラスを継承しています。
オーバーライド
オーバーライドとは、スーパークラスで定義しているメソッドを、同じ名前でサブクラスで再定義することです。
スーパークラスで定義したメソッドと目的は同じであるが、処理が異なるメソッドを定義する場合に使用します。
@override
アノテーションを使用して、意図的にメソッドをオーバーライドしていることを示すことができます。
class SmartTelevision extends Television { @override void turnOn() {...} // ··· }
暗黙的インターフェイス
インターフェースとは
インターフェースとは簡単に言うと、公開されたクラスの取り決めのことです。
ほとんどのインターフェイスは処理内容を含まない抽象クラスから作成されます。
DartにはJavaのように明示的にインターフェイスを宣言するinterface
キーワードはありません。
インターフェースを実装する場合、extends
ではなくimplements
キーワードを使用します。
// 抽象クラスA abstract class クラスA { void someMethod(); } // クラスAインターフェースの実装 class クラスB implements クラスA { void someMethod() { 処理内容 } }
※JavaやKotlinのinterface
、Swiftのprotocol
にあたります。
暗黙的インターフェース
前述の通り、Dartには明示的なインターフェースはありません。
代わりに、任意のクラスをインターフェースとして使用できます。
これはすべてのクラスにあらかじめ暗黙的にインターフェースが定義されているためです。
次のコードではImpostor
クラスはPerson
クラスを実装(implements
)しています。
Impostor
クラスはPerson
クラスのインターフェースをすべて実装する必要があります。
// 暗黙的インターフェイスPersonにはgreet()メソッドが含まれています class Person { final String _name; Person(this._name); String greet(String who) => 'Hello, $who. I am $_name.'; } // Personインターフェースの実装 class Impostor implements Person { String get _name => ''; String greet(String who) => 'Hi $who. Do you know who I am?'; } String greetBob(Person person) => person.greet('Bob'); void main() { print(greetBob(Person('Kathy'))); print(greetBob(Impostor())); }
実行結果
Hello, Bob. I am Kathy. Hi Bob. Do you know who I am?
列挙型
列挙型とはいくつかの定数をひとまとまりにして扱えるようにしたもので、プログラマが任意に定義できます。
enum
キーワードを使用して列挙型を宣言します。
次のコードはswich文で列挙型を使ったコードです。
※列挙型のすべての値を条件に含めないと警告が出ます。
enum Color { red, green, blue } void main() { var aColor = Color.blue; switch (aColor) { case Color.red: print('Red as roses!'); break; case Color.green: print('Green as grass!'); break; default: print(aColor); } }
実行結果
Color.blue
クラス変数
クラス変数やクラスメソッドは、クラス全体で扱うデータを格納するために使用します。
クラス変数、クラスメソッドはそれぞれstatic変数、staticメソッドとも呼ばれます。
呼び出す際には、クラス名.static変数名
やクラス名.staticメソッド名()
で呼び出します。
実装する際はstatic
キーワードを指定します。
class Queue { static const initialCapacity = 16; // ··· } void main() { assert(Queue.initialCapacity == 16); }
クラスメソッド
クラスメソッドはインスタンス上では動作しないので、this
にアクセスすることはできません。
しかし、クラス変数にはアクセスできます。次の例では、クラス上で直接スタティックメソッドを呼び出しています。
import 'dart:math'; class Point { double x, y; Point(this.x, this.y); static double distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } } void main() { var a = Point(2, 2); var b = Point(4, 4); var distance = Point.distanceBetween(a, b); assert(2.8 < distance && distance < 2.9); print(distance); }
実行結果
2.8284271247461903
ジェネリクス(総称型)
以前、List型の解説でList<String>のように<>内に型を指定していました。
ジェネリクス型でクラス定義をおこなうことにより、クラス内で型を事前に決めるのではなく、クラス利用時に決定できます。
次のコードはmain()
関数内で指定したString型の値とint型の値を同じクラス定義であつかっています。
class GenericMember<T> { final List<T> _memberList = []; void push(T item) => _memberList.add(item); T pop() => _memberList.removeLast(); get memberList => _memberList; } void main() { final memberA = GenericMember<String>(); memberA.push("Ken"); memberA.push("Bob"); //memberA.push(1); print(memberA.memberList); final memberB = GenericMember<int>(); memberB.push(1); memberB.push(2); memberB.push(3); print(memberB.memberList); memberB.pop(); print(memberB.memberList); }
実行結果
[Ken, Bob] [1, 2, 3] [1, 2]
クラス名に続く<>はダイヤモンド演算子と呼ばれており、T
のことを型パラメータといいます。
<T>の他に<E>や<K, V>などがあり、これらは汎用的な型、つまり、仮の型を使用することを表すために使用します。
型パラメータの名前は任意でかまいませんが、通常は意味のある大文字1文字が使用されます。
※TはType、EはElement、KはKey、VはValueの頭文字を意味しています
ジェネリクス型を使用することによりシンプルで安全なコードが実現できます。
ライタープロフィール
【N】
Webエンジニアの経験を経て、アプリエンジニアとしてEDAに入社。
Flutter開発導入のファシリテーターとして、勉強会などを担当している。