
みなさまこんにちは〜!
メモリアインクのすだです。
今回は、Kotlin特有の機能である「data class」を使うメリットや実際の使い方について、通常classの仕様と比較しながら解説していきます。
この記事を読んでわかること…
・data classとは?
・なぜdata classを使うのか
・data classの使い方について
環境
- Kotlin (ver 1.9.0)
- Android Studio (Giraffe | 2022.3.1 Patch 3)
classとdata classの基本
通常のclassとは?
classは、データ(情報)と振る舞い(処理)をひとまとめにした“設計図”のことです。
オブジェクト指向プログラミングにおける考え方の中核を担っています。
・クラス = 設計図(どんな情報・動作を持つか)
・オブジェクト(インスタンス) = 実体化したもの(クラスから生まれた具体的な存在)
通常のclassは「ロジック(処理)を中心に持つオブジェクト」を定義するのに使います。
Kotlinでは、たとえば以下のように書きます。
class ProductManager(val name: String, val price: Int, val stock: Int) {
fun showProductInfo() {
println("商品名: ${name}")
println("価格: ¥${price}")
println("在庫数: ${stock}")
}
fun purchase(quantity: Int): Boolean {
// 商品購入時処理
}
fun restock(amount: Int) {
// 商品在庫補充処理
}
}
商品に関する処理をまとめた設計図です。
「購入」時に走る処理と、「在庫補充」時に走る処理をそれぞれ関数としてこのクラスに詰め込んでいます。
data classとは?
Kotlin特有の機能であるdata classは、その名の通り「データを表現するためのクラス」です。
プログラムの中で、状態(プロパティ)の保持が主な目的であり、
複雑な処理を担うわけではない“単なるデータの入れ物”のようなクラスをシンプルに記述するための機能です。
以下のように、data という修飾子をつけてclassを定義します。
data class Product(
val name: String,
val price: Int,
val stock: Int
)
通常classのような細かい処理は基本的に書きません。
あくまでデータの入れ物として存在させます。
上記のようにProduct というdata class(データの入れ物)を作ると、
通常classで以下のように利用することができます。
class ProductManager(var product: Product) {
fun getProduct(): Product = product
fun showProductInfo() {
println("商品名: ${product.name}")
println("価格: ¥${product.price}")
println("在庫数: ${product.stock}")
}
fun purchase(quantity: Int): Boolean {
// 商品購入時処理
}
fun restock(amount: Int) {
// 商品在庫補充処理
}
}
ProductManagerのコンストラクタでProductを受け取ることによって
データを処理に利用できるのです。
なぜわざわざdata classを使うのか
→これは、「データの中身を扱う処理を、少ないコードで、安全に、正しく、自動で実装してくれるから」です。
たとえば、通常のclassだと、
class Product(val name: String, val price: Int, val stock: Int)
fun main() {
val p = Product("りんご", 100, 10)
println(p)
}
// 出力結果 ▼
Product@5e91993f
このように、Productを出力結果は「Productクラスのオブジェクトのメモリ上の住所」を表すだけで、中身がわかりません。
この場合、どうしたら「Productのデータを読める形で出力できるか」を考えたとき、以下のように実装します。
class Product(val name: String, val price: Int, val stock: Int) {
override fun toString(): String {
return "Product(name=$name, price=$price, stock=$stock)"
}
}
// 出力結果 ▼
Product(name=りんご, price=100, stock=10)
このように、通常classだと処理を書く(toString()をoverrideして実装する)必要があります。
しかしdata classではtoString()の処理を自動生成してくれます。
data class Product(val name: String, val price: Int, val stock: Int)
fun main() {
val p = Product("りんご", 100, 10)
println(p)
}
// 出力結果 ▼
Product(name=りんご, price=100, stock=10)
data class を使う一番の理由は、よく使う処理を自分で書かずに済むという点です。
上記のように、Kotlinが自動で便利な関数をいくつか生成してくれるため、
短くて安全なコードを書くことができます。
それでは、具体的にどんな関数が自動で用意されているのか、次のタイトルで見ていきましょう↓
data classで自動生成される便利な関数
data classには以下の「便利な関数」が用意されており、
これらの機能を自動でサポートしてくれます。
関数名 | 説明 |
---|---|
toString() | 文字列表示 |
equals() | 内容ベースでの比較が可能(同じ値ならtrue) |
hashCode() | SetやMapで同値と判断される |
copy() | 一部だけ変更してインスタンス複製が可能 |
componentN() | 分解宣言に使える(例:val (name, price, stock) = product ) |
toString()
オブジェクトの内容を文字列としてわかりやすく表示してくれる関数。
普通の class では Product@abcdef
など分かりづらい出力になる(= 別で実装が必要になる)ところ、
data class を使うとtoStringが自動サポートされます。
実装例:
data class Product(val name: String, val price: Int)
fun main() {
val product = Product("りんご", 100, 10)
println(product)
}
出力結果:
Product(name=りんご, price=100, stock=10)
equals()
2つのオブジェクトの「中身(プロパティの値)」が同じなら、等しいと判断してくれます。
通常の class では ==
は「同じ場所のデータか?」しか見ませんが、
data class は「中身が同じか?」で判断します。
実装例:
data class Product(val name: String, val price: Int)
fun main() {
val p1 = Product("りんご", 100, 10)
val p2 = Product("りんご", 100, 10)
val p3 = Product("バナナ", 80, 5)
println(p1 == p2) // true
println(p1 == p3) // false
}
hashCode()
オブジェクトを一意に識別するハッシュ値を返す関数です。Set
や Map
のようなコレクションで、重複チェックに使われます。
実装例:
data class Product(val name: String, val price: Int)
fun main() {
val productSet = hashSetOf(
Product("りんご", 100, 10),
Product("バナナ", 120, 5)
)
val searchTarget = Product("りんご", 100, 10)
if (productSet.contains(searchTarget)) {
println("同じ商品があります")
}
}
copy()
既存のオブジェクトから、一部のプロパティだけ変更して新しいインスタンスを作ることができます。
データの一部だけを変えて再利用したいときに非常に便利です。
実装例:
data class Product(val name: String, val price: Int)
fun main() {
val apple = Product("りんご", 100)
val anotherApple = apple.copy(price = 120)
}
println(anotherApple)
出力結果:
Product(name=りんご, price=120)
componentN()
プロパティを 分解(Destructuring) して変数に一発で取り出せる関数です。component1()
, component2()
, … がプロパティの順番で自動生成されます。
分解宣言とは:
「オブジェクトのプロパティを、個別の変数に一括で取り出す構文」のことです。
以下のように記述します。
val (a, b, c) = someDataObject
これは、内部的にはこう動いています↓
val a = someDataObject.component1()
val b = someDataObject.component2()
val c = someDataObject.component3()
data classでは、分解宣言に必要なcomponentN()
関数が自動的に生成されるため、分解宣言が可能になります。
実装例:
data class Product(val name: String, val price: Int, val stock: Int)
fun main() {
val productList = listOf(
Product("りんご", 100),
Product("バナナ", 120),
Product("みかん", 80)
)
for ((name, price) in productList) {
println("商品名: $name / 価格: ¥$price")
}
}
出力結果:
商品名: りんご / 価格: ¥100
商品名: バナナ / 価格: ¥120
商品名: みかん / 価格: ¥80
より実用的な使い方
たとえば、data classを「APIのJSONをオブジェクト化するためのクラス」として実装してみます。
JSON:
[
{
"id": 1,
"name": "りんご",
"price": 100,
"stock": 10
},
{
"id": 2,
"name": "バナナ",
"price": 120,
"stock": 5
}
]
Kotlin側でパース:
data class ProductResponse(
val id: Int,
val name: String,
val price: Int,
val stock: Int
)
fun main() {
val jsonList = """[{"id":1,"name":"りんご","price":100,"stock":10},{"id":2,"name":"バナナ","price":120,"stock":5}]"""
val productList = gson.fromJson(jsonList, Array<ProductResponse>::class.java).toList()
productList.forEach {
println("商品: ${it.name} / 価格: ¥${it.price}")
}
}
まとめ
おつかれさまでした。いかがでしたでしょうか!
data classは、「データの比較・表示・複製」などを簡単かつ安全に行いたいときに有効活用できる機能です。
ぜひ覚えて使ってみてください!



技術者としてのキャリアパスを次のレベルへと進めたい皆様、
<未経験からIT・Webエンジニアを目指すなら【ユニゾンキャリア】>
自分の市場価値をさらに向上させてみませんか?
それではまた次回の記事でお会いしましょう!
コメント