鼻間違ったクラスを定義するキーワードは class
です。
たまに object
キーワードでクラスを定義する場合を見ることができます。
object
でクラスを定義すると、シンクルトン(Singleton)パターンが適用され、オブジェクトが一度だけ生成されるようにします。
Javaではシングルトンパターンを適用するために、かなり多くのコードを記述する必要がしたんです、鼻間違ったでは object
を使用すると、このような形式的なコード(boilerplate)を作成しなくてもされます。
シングルトンとして使用する方法に加えて、objectは匿名のオブジェクトを作成するときにも使用されます。
objectの使用する理由を調べ、それぞれについてサンプルコードを見てみましょう。
シングルトンクラスを定義するためのobjectの例
objectでシングルトンクラスを定義することができます。
次のコードでCarFactoryクラスを定義するときに class
があるべき位置にobject
を入力してくれれば、このクラスはシングルトンで動作するようになります。
object CarFactory {
val cars = mutableListOf<Car>()
fun makeCar(horsepowers: Int): Car {
val car = Car(horsepowers)
cars.add(car)
return car
}
}
class Car(power: Int) {
}
次のコードのように CarFactory.makeCar
ようメソッドにアクセスしてCarオブジェクトを生成することができます。また、 CarFactory.cars
のように直接変数にアクセスすることができます。
CarFactoryオブジェクトはシングルトンで実装がされているので、何度も呼び出してもCarFactoryオブジェクトは一度だけ生成がされます。
val car = CarFactory.makeCar(150)
println(CarFactory.cars.size)
上記のコードを見ると、CarFactory.makeCar()は、staticメソッドを呼び出しているように見えます。 Javaでどのように変換がされるか見ればシングルトンが内部的にどのように実装されるかを理解することができます。
public final class CarFactory {
private static final List cars;
public static final CarFactory INSTANCE;
public final List getCars() {
return cars;
}
public final Car makeCar(int horsepowers) {
Car car = new Car(horsepowers);
cars.add(car);
return car;
}
static {
CarFactory var0 = new CarFactory();
INSTANCE = var0;
cars = (List)(new ArrayList());
}
}
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
Car car = CarFactory.INSTANCE.makeCar(150);
int var2 = CarFactory.INSTANCE.getCars().size();
System.out.println(var2);
}
上記のJavaで変換されたコードを見ると、CarFactoryオブジェクトはINSTANCEというstaticオブジェクトを生成します。
そして、このオブジェクトにアクセスするとき CarFactory.INSTANCE
を介してアクセスされます。 INSTANCEはstaticで生成されるため、プログラムがロードされるときに生成されます。だからスレッド安全性(thread-safety)が保証されますが、内部的に共有リソースを使用する場合、スレッドの安全性が保証されていないため、同期(synchronization)コードを作成する必要があります。
シングルトンクラスを定義するためcompanion object例
上記の例では、CarFactoryオブジェクトがCarオブジェクトを生成してくれる実装でした。ここファクトリーパターンと、シングルトンパターンが一緒に適用置く。 Carクラスの中Factoryパターンを定義したいことができます。
Car.makeCar
よう呼び出しをすることが直感的に見栄えからです。こういうときは、companion objectとして宣言してくれればされます。
このようにCar中companion objectでFactoryを定義してくれれば Car.makeCar()
のように呼び出すことができます。
事実 Car.Factory.makeCar()
の呼び出してくれることが明示的に正確な表現ですが、鼻間違っは便宜のためにFactoryを省略することができるようにしてくれました。
class Car(val horsepowers: Int) {
companion object Factory {
val cars = mutableListOf<Car>()
fun makeCar(horsepowers: Int): Car {
val car = Car(horsepowers)
cars.add(car)
return car
}
}
}
fun main(args: Array<String>) {
val car = Car.makeCar(150)
val car2 = Car.Factory.makeCar(150)
println(Car.Factory.cars.size)
}
変換されたJavaコードを見れば、Carクラスの中に入れ子になったクラス(nested class)でFactoryクラスが定義されています。
また、 Car.Factory
クラスはFactoryという名前のstaticオブジェクトとして宣言しました。
外部から Car.Factory.makeCar
のように使用することができているが、ここでFactoryは、クラス名ではなく、static変数の名前でした。
鼻間違っからmakeCarを二つの方法で呼び出しましたが、Javaでは同じコードで呼び出していることを知ることができます。
public final class Car {
private final int horsepowers;
private static final List cars = (List)(new ArrayList());
public static final Car.Factory Factory = new Car.Factory((DefaultConstructorMarker)null);
public final int getHorsepowers() {
return this.horsepowers;
}
public Car(int horsepowers) {
this.horsepowers = horsepowers;
}
public static final class Factory {
@NotNull
public final List getCars() {
return Car.cars;
}
@NotNull
public final Car makeCar(int horsepowers) {
Car car = new Car(horsepowers);
((Car.Factory)this).getCars().add(car);
return car;
}
private Factory() {
}
// $FF: synthetic method
public Factory(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
Car car = Car.Factory.makeCar(150);
Car car2 = Car.Factory.makeCar(150);
int var3 = Car.Factory.getCars().size();
System.out.println(var3);
}
objectを匿名オブジェクトとして使用した例
objectは、匿名のオブジェクトを定義する際にも使用されます。匿名オブジェクトは名前がないオブジェクトで、一度だけ使用されて再利用されない場合に使用します。 再利用されないので、気にクラス名をつけてくれないのです。
たとえば、以下のようにVehicleインタフェース、start()メソッドが定義されています。 start()は、Vehicleオブジェクトを引数として渡されます。
interface Vehicle {
fun drive(): String
}
fun start(vehicle: Vehicle) = println(vehicle.drive())
次のコードでは、start()の引数として渡される object : Vehicle{...}
は、匿名オブジェクトです。
この匿名オブジェクトはVehicleインタフェースを継承したクラスをオブジェクトとして生成されたことを意味します。匿名オブジェクトであるため、クラス名はなく、実装部 {...}
の中に定義する必要があります。
start(object : Vehicle {
override fun drive() = "Driving really fast"
})
上記のコードでも、Javaに変換してみましょう。 Javaのも鼻間違ったようVehicleを継承した匿名のオブジェクトを作成しました。
(参考までに、鼻間違っから fun start()
はファイルのTop-levelに定義されているため、Javaでstaticメソッドで生成がされました。)
public interface Vehicle {
@NotNull
String drive();
}
public final class KotlinKt {
public static final void start(@NotNull Vehicle vehicle) {
String var1 = vehicle.drive();
System.out.println(var1);
}
public static final void main(@NotNull String[] args) {
start((Vehicle)(new Vehicle() {
@NotNull
public String drive() {
return "Driving really fast";
}
}));
}
}
まとめ
objectの使用方法について説明しました。 クラスを定義する際にobjectを使用すると、シングルトンパターンが適用され、objectを使用して、匿名のオブジェクトを作成することもできました。 もしJava開発者であれば、objectコードがどのようにJavaに変換されるか確認することをお勧めします。鼻間違ったコードの理解度を高めることができます。
参考
Related Posts
- Kotlin - エルビス演算子(Elvis Operation)
- Kotlin - Timer、定期的に関数を実行する
- Kotlinで正規表現を使用する
- Kotlin - 文字列の比較方法(equals、==、compareTo)
- Kotlin - 2つのList一つの併合
- Kotlin - ディレクトリのすべてのファイルのリスト出力
- Kotlin - リストの並べ替え方法(sort、sortBy、sortWith)
- Kotlin - 乱数生成(Random、SecureRandom)
- Kotlin - StringをFloatに変換
- Kotlin - Destructuring Declaration
- Kotlin - objectとclassキーワードの違い
- Kotlin - 現在の日付と時刻を取得する方法