Androidアプリ開発で効果音を鳴らすためにSoundPoolを導入した際、このようなエラーでビルドが通らなくなった経験はありませんか?
Property must be initialized or be abstractVariable 'soundPool' must be initialized
このエラーは、Kotlinの「プロパティは宣言と同時に初期化しなければならない」という強い制約が原因で発生します。
この記事では、SoundPoolでこのエラーが発生する原因と、状況に応じたスマートな解決策2選をコード例付きで分かりやすく解説します!
1. SoundPoolでエラーが出る原因とは?
Kotlinでは、クラスのプロパティ(変数)を宣言する場合、原則として「その場で初期化する」か「コンストラクタで初期化する」必要があります。
しかし、AndroidのSoundPoolは、Contextが必要だったり、インスタンスの生成タイミングをコントロールしたかったりするため、以下のように単純に宣言するとコンパイラに怒られてしまいます。
Kotlin
// ❌ これだと初期化されていないためエラーになる
class SoundManager {
private val soundPool: SoundPool
}
この問題を解決するには、「独自のマネージャークラスを作ってその場で初期化する」か、「lateinitを使って初期化を遅らせる」かの2つのアプローチが有効です。
2. 【ベストプラクティス】SoundManagerクラスを作って直接初期化する
最もおすすめな設計は、音声再生を管理する専用の「マネージャークラス(SoundManager)」を自作し、宣言と同時に初期化を完了させる方法です。
コンストラクタでContextを受け取るように設計すれば、プロパティの宣言と同時にSoundPool.Builder()を実行できます。
解決コード例
Kotlin
package com.example.MyApp
import android.content.Context
import android.media.AudioAttributes
import android.media.SoundPool
class SoundManager(private val context: Context) {
// ① 変数宣言と同時に SoundPool を直接ビルドして初期化
private val soundPool: SoundPool = SoundPool.Builder()
.setMaxStreams(5) // 同時に鳴らせる最大音数
.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
)
.build()
private var swipeSoundId: Int = 0
init {
// ② init ブロックの中で音声ファイルの読み込みを行う
swipeSoundId = soundPool.load(context, R.raw.se_swipe, 1)
}
// 音声を再生するメソッド
fun playSwipe() {
if (swipeSoundId != 0) {
soundPool.play(swipeSoundId, 1.0f, 1.0f, 1, 0, 1.0f)
}
}
// メモリ解放
fun release() {
soundPool.release()
}
}
この設計が優れている理由(SEO・可読性のメリット)
val(不変)にできる: 後から書き換えられないため、スレッドセーフで安全です。- Null安全:
SoundPool?のようにNull許容型にする必要がないため、呼び出し時にsoundPool?.play(...)のような冗長なコード(セーフコール)が不要になります。 - 役割の分離: インスタンス生成、リソース読み込み(
init)、再生処理の役割が綺麗に分かれており、メンテナンス性が抜群です。
3. 【代替案】MainActivityなどのライフサイクル内で初期化する場合(lateinit var)
もしマネージャークラスを作らず、MainActivity などの onCreate の中で SoundPool を初期化したい場合は、lateinit var を使用します。
Kotlin
class MainActivity : AppCompatActivity() {
// 後で初期化することを明示(初期化を遅らせる)
private lateinit var soundPool: SoundPool
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// onCreateの中で初期化を実行
soundPool = SoundPool.Builder()
.setMaxStreams(5)
.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
)
.build()
}
}
⚠️ 注意:
lateinitを使う場合は、初期化される前に変数にアクセスするとUninitializedPropertyAccessExceptionでアプリがクラッシュするため、必ずライフサイクルの初期段階で初期化を完了させてください。
4. まとめ:Kotlinの特性を活かした設計をしよう
Kotlinの Property must be initialized エラーは、一見不便に思えるかもしれませんが、「意図しないNullクラッシュを未然に防ぐ」ための強力な味方です。
- 専用クラスを作れるなら 「宣言と同時に直接ビルド」 が最も安全で綺麗
- Activityのライフサイクルに合わせるなら 「lateinit var」 で遅延初期化
あなたのプロジェクトの構造に合わせて、最適な方法を選んでみてください!


コメント