Android Gradle Plugin(AGP)9.0以降へ更新すると、Gradle Sync中に「ApplicationExtensionImpl cannot be cast to BaseExtension」というClassCastExceptionが発生することがあります。
これはAGP 9でAndroid DSLの実装が新しい公開インターフェースへ切り替わった一方、プロジェクトの独自ビルドロジックや外部Gradleプラグインが、廃止予定のcom.android.build.gradle.BaseExtensionへキャストしていることが原因です。
代表的なエラー
java.lang.ClassCastException:
class com.android.build.gradle.internal.dsl.ApplicationExtensionImpl
cannot be cast to class com.android.build.gradle.BaseExtension
エラーはプロジェクトのコンパイル前、Gradleの設定フェーズで発生することが多く、build.gradleの読み込みやプラグイン適用中に停止します。
なぜAGP 9で発生するのか
AGP 7.x~8.xでは、Android DSLの実装が旧BaseExtensionと新しい公開インターフェースの両方を実装していました。そのため、古いプラグインが次のように取得しても動作していました。
val android = extensions.getByType(
com.android.build.gradle.BaseExtension::class.java
)
AGP 9では新しい公開DSLインターフェースだけが使われ、内部実装は非公開の型へ変更されました。このため、ApplicationExtensionImplを旧BaseExtensionとして扱おうとするとキャストに失敗します。
キャッシュ破損ではなくAPI互換性の問題です。「Invalidate Caches」やgradlew cleanだけでは根本的に解決しません。
解決方法1:原因となる外部プラグインを更新する
スタックトレースに外部プラグイン名が表示されている場合は、そのプラグインをAGP 9対応版へ更新します。静的解析、Firebase関連、Flutter/React Nativeプラグイン、コード生成、APK名変更など、Android DSLへアクセスするプラグインが原因になることがあります。
./gradlew :app:tasks --stacktrace
./gradlew :app:assembleDebug --stacktrace
スタックトレース内で、最初に現れる自分のパッケージ名または外部プラグインのパッケージ名を確認します。AGP内部クラスの直前に表示される呼び出し元が手掛かりです。
解決方法2:独自Gradleプラグインを新DSLへ移行する
自作のConvention PluginやbuildSrcがBaseExtensionを参照している場合は、モジュール種別に対応した公開インターフェースへ変更します。
アプリモジュール
import com.android.build.api.dsl.ApplicationExtension
val android = extensions.getByType(
ApplicationExtension::class.java
)
android.compileSdk = 37
ライブラリモジュール
import com.android.build.api.dsl.LibraryExtension
val android = extensions.getByType(
LibraryExtension::class.java
)
android.compileSdk = 37
複数のAndroidモジュールへ共通設定を適用する場合も、内部実装クラスへキャストせず、AGPが公開しているApplicationExtension、LibraryExtensionなどを使用してください。
解決方法3:applicationVariantsをandroidComponentsへ移行する
旧BaseExtensionと一緒にapplicationVariants、libraryVariants、testVariantsなどを使用している場合は、新しいVariant APIへ移行します。
androidComponents {
onVariants(selector().all()) { variant ->
println("Variant: " + variant.name)
}
}
ビルドタイプを無効化する処理は、旧variantFilterではなくbeforeVariantsを使用します。
androidComponents {
beforeVariants(
selector().withBuildType("release")
) { variantBuilder ->
variantBuilder.enable = false
}
}
Variant APIは設定タイミングが旧APIと異なるため、単純な型名の置換だけでなく、公式のAGP Recipesも確認してください。
一時的な回避策:android.newDsl=false
必要な外部プラグインがまだAGP 9へ対応していない場合、AGP 9では旧DSL実装を一時的に有効化できます。
# gradle.properties
android.newDsl=false
これは移行期間用の回避策です。Googleは旧DSLへのオプトアウトをAGP 10.0で削除すると案内しています。プラグイン更新またはビルドロジック移行を進め、対応後はこの設定を削除してください。
Built-in Kotlinにも未対応のプラグインがある場合、別途android.builtInKotlin=falseが必要になるケースがありますが、両者は別の機能です。必要性を確認せず同時に無効化しないでください。
原因箇所が見つからない場合
- プロジェクト全体で
BaseExtensionを検索します。 buildSrc、build-logic、Convention Pluginを確認します。applicationVariantsやlibraryVariantsも検索します。--stacktraceで外部プラグインを特定します。- 一時的にプラグインを一つずつ外して原因を切り分けます。
./gradlew help --stacktrace
./gradlew :app:assembleDebug -Pandroid.debug.obsoleteApi=true
修正後の確認手順
BaseExtensionへの参照が残っていないことを確認します。- 外部プラグインがAGP 9対応版であることを確認します。
- Android StudioでGradle Syncを実行します。
./gradlew clean :app:assembleDebugを実行します。- DebugだけでなくRelease、テスト、商品フレーバーもビルドします。
android.newDsl=falseを外した状態で成功することを確認します。
注意点
AGPの内部型であるApplicationExtensionImplを直接参照する方法は避けてください。内部クラスは公開APIではなく、マイナーバージョン更新でも変更される可能性があります。
また、AGPを8系へ戻す方法は緊急回避にはなりますが、根本解決ではありません。利用しているプラグイン、Gradle、JDKの対応表を確認しながら新APIへ移行します。
まとめ
「ApplicationExtensionImpl cannot be cast to BaseExtension」は、AGP 9の新DSL実装を旧BaseExtensionへキャストしたために発生します。外部プラグインを更新するか、自作ビルドロジックをApplicationExtension・LibraryExtensionとandroidComponentsへ移行してください。android.newDsl=falseは一時的な回避策としてのみ使用します。
参考:Android Gradle Plugin 9.0 release notes/Write Gradle plugins/Android Gradle Plugin Recipes
