質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

2回答

14421閲覧

lateinitを使うのが正しいのでしょうか?

ohtakazuki

総合スコア31

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2017/10/09 02:36

編集2017/10/09 02:42

こんにちは。

端末で保持している画像を、カーソルを使って取得する処理があります。
(カーソルは、外部ストレージに対する権限許可が必要です)
AndroidOSが6.0以上でかつ権限許可が得られなかった場合、アプリを終了させます。
許可が得られた場合、カーソルを閉じる処理が必要です。

Javaであれば、onStopに

if (cursor != null ) cursor.close();

と書けば済んだのですが、Kotlinの場合、例外が発生します。

kotlin.UninitializedPropertyAccessException: lateinit property cursor has not been initialized

そこで、苦肉の策として、一旦カーソルを作ってfinishする、と書きました。

cursor = contentResolver.query( MediaStore.Images.Media.INTERNAL_CONTENT_URI, null, null, null, null)

これにより例外が発生しなくなりましたが、不要なコードを書いているようで、なんだかすっきりしません。
かといって、cursorをlateinitしない(宣言時になんらかのカーソルを割り当てる)のも、無駄なコードだと思います。
宣言時に、private var cursor : Cursor? = null とすると、null判定コードが各所に必要となり、Kotlin的に正しくない気がします。

どのように書くのが最善なのか、アドバイス頂ければ幸いです。

Activityの中のコード

private lateinit var cursor : Cursor private val PERMISSIONS_REQUEST_CODE = 100 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Android Version 6.0 later if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { initCursor() } else { requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PERMISSIONS_REQUEST_CODE) } } else { // Android Version under 6.0 initCursor() } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { when (requestCode) { PERMISSIONS_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { initCursor() } else { finishApp() } else -> finishApp() } } private fun finishApp() { // It prevents kotlin.UninitializedPropertyAccessException of cursor(lateinit valiant). It's last resort... cursor = contentResolver.query( MediaStore.Images.Media.INTERNAL_CONTENT_URI, null, null, null, null) /* if(cursor != null) cursor.close() */ finish() } override fun onStop() { super.onStop() cursor.close() } private fun initCursor() { cursor = contentResolver.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null) cursor.moveToFirst() }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

まず、この際はlateinitを使うのは本質的ではありません。
なぜなら、lateinitというキーワードを見たプログラマは、そのインスタンスは必ずどこかで初期化されていると思うからです。

もしもcursorが初期化されない状態があり得るのなら、それはオプショナルでprivate var cursor : Cursor? = nullと書くべきです。nullであり得ることを明示的にプログラマに示すので理解しやすいコードになります。

そして、問題の呼び出しですが、kotlinの安全呼び出しを使えば簡単に書くことができますのでそんなに手間だとは思いません。
さらに、プログラムを書く上で重要なのは、書く手間よりも読む手間を減らせということです。コードというものは圧倒的に書く時間よりも読む時間と回数が多くなるものです。ですので、多少面倒だな、と思っても面倒臭がらずに本質的、もしくは理解しやすいように書く精神は大切ですよ。(その上で、スマートな書き方を模索するのはとても良いことです!)

java

1private var cursor : Cursor? = null 2// 中略 3 private fun finishApp() { 4 cursor?.close() // cursorがnull出ない時だけ関数が実行される。 5 }

投稿2017/10/10 15:25

編集2017/10/10 15:29
hiramekun

総合スコア428

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ohtakazuki

2017/10/10 22:42

ありがとうございます。手間の問題では無いです。Cursor?を使った場合、各所でcursor?.と記述する必要が出てきます。そうすると、仮にcursorがnullだった場合、?.以降の処理は実行されないことになります。Javaのnullチェックの代わりが?.であれば止むを得ないのですが、kotkinのnon-null型の特性を活かし、nullチェックをしなくてもコードを保証できるようにできればいいなと思います。 どうやら私の考えで行くと、var cursor宣言時に、なんらかの値を設定するしかなさそうですね。。。(その後、実際にcursorを使う際またcontentResolverから取得した値を設定するので、宣言時のコードは意味のないものとなってしまいますが。。。)
hiramekun

2017/10/11 01:12

はい、おっしゃる通り、各所でcursor?.と記述する必要が出てきます。ここで問題になるのは、「kotlinのnon-null型の特性を生かす」とおっしゃっている部分です。このactiviy内では、cursorが生成されない場合と生成される場合の2通りがあります。cursorが生成されない可能性があるのならば、non-nullとして扱うべきではないと思います。この時のnullは、「cursorは初期化されない状態だよ」という情報として扱います。 ohtakazukiさんのおっしゃるように、kotlinのnon-null型の特性を活かすということは重要な考えですが、それはnon-nullとして扱う必要性のある時です。 今回はnull許容型として扱うべきであり、おっしゃっているように「Javaのnullチェックの代わりが?.」という考えをするべきだと思います。
ohtakazuki

2017/10/11 01:31

そうですね。おっしゃられるように、null許容型を使うべき時とそうでない時を分ける、というのがKotlinのnon-null型の特性を活かすことになりますね。ありがとうございます。
guest

0

現状lateinitを使った場合上で初期化されているかどうかがわからないため上のように不要な場合でも初期化してやるひつようがあります。これはlateinitを使っている以上仕方ないです。

僕個人の意見としてはprivate var cursor : Cursor? = nulの方を使った方が好きですね。
確かにnull判定をする必要がありますが、スコープ関数を適切に使ってやればスッキリする気がします()。
例えば下のような感じです。

private var cursor : Cursor? = null // 中略 private fun finishApp() { //cursorがnon-nullのときにletの中のブロックが実行される cursor?.let{ cursor -> //このブロックのなかではcursorはnon-null cursor.close() } }

投稿2017/10/09 13:47

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ohtakazuki

2017/10/10 00:46

なるほど。ありがとうございます! 確かにスコープを使えばきれいに書けますね。 私的には、non-null型を持てる言語特性を活かしたいところはあります。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問