Kotlin ジェネリクスの型投影 変位指定 共変でout Anyとは をサンプルコードと共にめちゃ簡単に説明してみる

Kotlin勉強中の私です。Javaから移行組です.

今回、次のようなソースコードを見ていて、疑問に思ったことがいくつかありました。出典は、Android StudioでLoginの機能を自動的に作ってみたところ、Loginの結果を表示するクラスとして、下記のResultクラスが存在しています。

sealed class Result<out T : Any> {

    data class Success<out T : Any>(val data: T) : Result<T>() 
    data class Error(val exception: Exception) : Result<Nothing>()

    override fun toString(): String {
        return when (this) {
            is Success<*> -> "Success[data=$data]" 
            is Error -> "Error[exception=$exception]"
        }
    }
}

特に気になったのは

data class Success<out T : Any>(val data: T) : Result<T>() 

の部分ですね。

ジェネリクスは、Javaでも使っていたので知ってはいましたが、 out ?? ってなりました。

そこで、初めて「変位指定」とか「型投影」とか「不変」「共変」「反変」という言葉たちに出会いました。

もう、いきなりつまずきそうですよね?!字からして、難しそうで、逃げたくなりますよね??

「共変とはなんぞや?変位指定とはなんぞや!?」

と独眼鉄になっちゃいますね(´ω`)

しかも、説明を読んでも、さっぱりわかりませんでした。
特に、outがスーパータイプを指定することはわかりましたが、Anyって指定すると、なんでもいいわけじゃん?なんで必要なの?となりました。

Kotlin公式サイト ジェネリクス
https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/generics.html

自分でコード書いてみたら、腑に落ちました。(もっと早くやれよ)

class Container<T>(var value:T)

上記のように、Containerクラスを作ります。とってもシンプルなクラスですね。ただ単に、入れ物です。value というプロパティだけがあります。

下記のようなshowというメソッドがあって、引数にContainerを指定します。(Android Studioで実行しています。)

    
fun show(container:Container<out Any>){
        Log.d(TAG, container.toString())
}

show()を呼び出します。

val inta:Container<Int> = Container(23)
show(inta)

val stringa:Container<String> = Container("うきき")
show(stringa)

今、ここでIntもStringもshowの引数として入れられましたね?

では、showメソッドの引数を、次のように引数の型指定から「out」をなくしてみます。

 fun show(container:Container<Any>){ //この行からoutがなくなっています。
Log.d(TAG, container.toString())
}
val inta:Container<Int> = Container(23)
show(inta)

val stringa:Container<String> = Container("うきき")
show(stringa)

すると、呼び出し元のshow(inta)、show(stringa)でコンパイルエラーが起こります。

Type mismach.Required Containre<Any>

って出ます。

ああ。つまり、outを指定するのは、Anyのサブタイプならなんでもいいよってためにつけるわけですね。٩( ‘ω’ )و

「ウォーーーーーターーーーー!!!」

とヘレンケラーが叫んだ瞬間もこんな感じだったでしょう。(大げさ)

これが、共変です。

反変はその反対です。(ざっくり)

不変は、outを消したときと同様です。

それにしても共変って日本語わかりにくくないですか??!「共に」「変わる」ってなんだ?オラさっぱりわからなかったぞ!!

初心者なので、理解が間違っているところがあるかもです。(´;ω;`)
ぜひぜひご指摘くださーい。

versions.gradle にバージョンを列記する

完全にただのメモなんですが。

Androidのサンプルコード見てたら、アプリケーションのgradle(build.gradle Module:app)に次のように書いてあって、?!と思いました。

android {
compileSdkVersion build_versions.target_sdk
buildToolsVersion build_versions.build_tools
///中略
}

build_versions.target_sdk

ってどこで定義されてるのかな?って思ったら、versions.gradleというファイルがあって、そこにバージョンたちが列記されていました。


def build_versions = [:]
build_versions.min_sdk = 14
build_versions.target_sdk = 28
build_versions.build_tools = "28.0.3"
ext.build_versions = build_versions

すごい。バージョンなどをいじるときに、アプリケーションのgradleファイルをいじらなくていいんですね。

設定は、プロジェクトのgradleファイル(build.gradle(Project:BasicSample)に次のように書いたらよいようです。

buildscript {
apply from: 'versions.gradle'
}


ローカルの開発環境(Vagrant)のPHPでcURLが機能しない

タイトルがわかりにくいですね。

困った

こんなことをしていました。

$url = 'http://' . $_SERVER['HTTP_HOST'] . SERVER_URL . 'index.php?action=hoge';

$curl = curl_init($url);
    (中略)
$response = curl_exec($curl);
※$url: http://local-host.name/server_url/index.php?action=hoge な感じ

こんな風になりました。

Could not resolve host: local-host.name

いやぁ繋がらん繋がらん…… 軽く調べたらDNSがまずいみたいな話🤔

解決

vagrant上の/etc/hosts127.0.0.1 local-host.nameを追加して、$ vagrant reloadしたら解決していた。

[vagrant@localhost ~]$ sudo vim /etc/hosts
    (~編集~)
[vagrant@localhost ~]$ sudo cat /etc/hosts

127.0.0.1 local-host.name

[vagrant@localhost ~]$ exit
Abgemeldet
Connection to 127.0.0.1 closed.

C:\hoge\vagrant\piyo>vagrant reload
~~~

ちょろいもんですわ。(反映されるタイミングが少し怪しかったけど……)

参考

Smart cast to “TextView” is impossible, because “textView” is a mutable property…

JavaからKotlinへ移行してAndroidアプリ書いている、私のような人にはタイトルのようなエラー結構出ると思うんですけど。

下記のようなシンプルなコードがあったとします。

    private var textView: TextView? = null

    //アプリが開始されると最初に処理されるメソッド
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.content_input_voice)

        textView = findViewById<View>(R.id.textView) as TextView

        textView.setText(getText(R.string.no_task_error)) //ここでエラー
}

これは、結論から言いますと、次の2パターンで治ります。

//パターン1 textViewはnull許可だってわかってるよ?とする
textView?.setText(getText(R.string.no_task_error)) 
//パターン2 textViewをvalにする
val textView:TextView = findViewById<View>(R.id.textView) as TextView
textView.setText(getText(R.string.no_task_error))

mutable は可変という意味です。

varという宣言していたり、Null許可している変数にアクセスしようとすると出ます。


Ubuntu18.04 LTS | HDDの増設

前回の続き。

内蔵HDDの追加作業をしました。

調べたらGUIで簡単にできるらしい。さっくりと実践。
(うっかりスクショ撮り忘れたので文字ばっかですがご了承ください)

参考: HDDをフォーマットする – Ubuntu 18.04 LTS編

  1. Windowsキーとか押して「disk」と入力し、出てきた「ディスク」を開く。
  2. セットアップを行いたいディスクを選択し、メニュー(≡)から「ディスクを初期化」を選択。
  3. ディスクの初期化
    何も入っていないドライブだったので下記のように選択して初期化。
      消去: 既存のデータを上書きしない(クイック)
      パーティション: 新しいシステムと(中略)互換(GPT)
  4. [➕]を押して未割り当て領域にパーティションを作成
    パーティション作成ダイアログでは、パーティションのサイズを最大にしました。
  5. ボリューム名などの設定は以下のようにして作成。
      ボリューム名: data
      消去: オフ
      タイプ: Linuxシステム専用の内蔵ディスク(Ext4)。
  6. [▶]を押してパーティションをマウント
  7. [⚙]を押してマウントオプションを編集
    Ubuntu起動時に自動でマウントされるよう設定する。
      ユーザーセッションのデフォルト: オフ
      ☑ システム起動時にマウントする
      ☑ ユーザーインターフェースに表示する
      マウントポイント: /media/onlineconsultant/data
      識別: UUID=~~~~~~~~~~
      ファイルシステムのタイプ: auto
    として [🆗]。認証を求められるのでパス入れて完了。
  8. 再起動して確認。

サイト通りの手順でできてしまった。

一応コマンドでもできるという話

まず現状確認

$ cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sda1 during installation

UUID=d2916135-4486-43be-a209-b548c96bfe38 / ext4 errors=remount-ro 0 1
/swapfile none swap sw 0 0

ここで、以下のようにコマンド実行

sudo echo -e '/dev/sda1 /media/onlineconsultant/data auto defaults 0 0' >> /etc/fstab

みたいなことをしてもできます。(ちょっと /etc/fstab ファイルの中身は違うけど実質同じ(はず))

おわり。

参考