default eye-catch image.

とりあえずチャットアプリを作ってみる

Kotlin製のChat UI Libraryを写経しながらAndroidアプリを作る練習。 もちろんサーバ側は無いので、文字列決め打ちで出してるだけ。 昔作ったJava製のものと基本的なことは変わっていない様子。 https://github.com/bassaer/ChatMessageView.git null安全性の絶対的な安心感。これに尽きる。。 自分のような素人が書いてもぬるぽで落ちないというのは奇跡!。 Swiftとそっくりという噂なので、 もしかするとKotlinからSwiftに移植するのは簡単なのかも。

default eye-catch image.

一覧から詳細の起動_一覧側(MainActivity)

一覧から詳細を起動する一覧側(MainActivity.kt)の実装。 ListViewのsetOnItemClickListener()の書き方。 setOnItemClickListener()の引数はAdapterView.OnItemClickListenerオブジェクトを取る。 OnItemClickListenerオブジェクトは1つの仮想関数(Single Abstract Method)を持っていて、 それを実装したものを渡すことになる。 本来、OnItemClicListenerを派生したオブジェクトでSAMを実装したものを渡す、という とても長い記述になるのだけれども、仮想関数が1個だけならば代わりにラムダ式を1個渡せば良い。 (SAM変換)。 ActivityからIntentを取得してIntentからActivityを起動する、という書き方。 Intentを取得するときにパラメタ(今回の場合はArticleインスタンス)が渡る。 package com.example.ikuty.myapplication import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.widget.ListView import com.example.ikuty.myapplication.model.Article class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val listAdapter = ArticleListAdapter(applicationContext) val dummyArticle1 = dummyArticle(\"ikuty.com記事1\",\"ikuty1\") val dummyArticle2 = dummyArticle(\"ikuty.com記事2\",\"ikuty2\") listAdapter.articles = listOf(dummyArticle1,dummyArticle2) val listView: ListView = findViewById(R.id.list_view) as ListView listView.adapter = listAdapter listView.setOnItemClickListener { adapterView,view, position, id -> val article = listAdapter.articles[position] ArticleActivity.intent(this, article).let { startActivity(it) } } } }

default eye-catch image.

一覧から詳細の起動 – ParcelableなインスタンスをIntentに付与してActivityを起動する

記事一覧画面でアイテムを触るとそのアイテムの詳細画面が開くというのをやりたい。 記事詳細画面のController(Activity)を書く。 Intent まずIntent。Activityを起動する際にパラメタの役割を果たす。 Intentには「意図」「目的」の意味がある。Activityを起動する意図とか目的とか。 ベースとなるIntentに付加的な情報を付与(putExtra)することで「意図」「目的」となる。 Intentには基本型の他にカスタムオブジェクトを付与することができる。 ただし、カスタムオブジェクトはParcelableインターフェースを実装している必要がある。 あるActivityから別のActivityを起動する際のテクニックとして、 起動される側のActivityが持つintent()メソッドを使ってIntentを作成し(つまりパラメタを渡し)、 作成したIntent経由でActivityを起動する。 companionオブジェクトにfun intent(context: Context, article: Article): Intentを作る。 このintent()を経由すれば起動に必要なパラメタ(今回はarticle: Article)を付与したIntentを取得できる。 intent()内ではputExtra()を使ってarticleとIntentを紐づけている。 onCreate(savedInstanceState: Bundle?) onCreate()が発火したときにはIntentにParcalableなArticleインスタンスが付与済み。 Activityが持つintentからgetParcelableExtra()によりArticleインスタンスを取得して、 ビューにそのArticleインスタンスを表示する。 package com.example.ikuty.myapplication import android.content.Context import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.webkit.WebView import com.example.ikuty.myapplication.model.Article import com.example.ikuty.myapplication.view.ArticleView class ArticleActivity: AppCompatActivity() { companion object { private const val ARTICLE_EXTRA: String = \"article\" fun intent(context: Context,article: Article): Intent = Intent(context, ArticleActivity::class.java) .putExtra(ARTICLE_EXTRA, article) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_article) val articleView = findViewById(R.id.article_view) as ArticleView val webView = findViewById(R.id.web_view) as WebView val article: Article = intent.getParcelableExtra(ARTICLE_EXTRA) articleView.setArticle(article) webView.loadUrl(article.url) } }

default eye-catch image.

リストビューを表示するためにListViewAdapterを実装する

ListViewAdapter リストビューを表示するためにListViewAdapterを実装する。 BaseAdapterを継承して必要なメソッドを実装する。 実装が必要なのは以下。 fun getCount(): Int fun getItem(position: Int): Any? fun getItemId(position: Int): Long fun getView(position:Int,convertView: View?,parent: ViewGroup?): View fun getCount(): Int 名前の通り、リストビューが保持するアイテムの個数を返す。 ListViewAdapterが保持するList<Article&gtのサイズを返すようにする。 fun getItem(position: Int): Any? 名前の通り、指定した位置のアイテムを返す。 保持するList<Article&gtの要素を返すようにする。 fun getItemId(position: Int): Long 指定した位置のアイテムに関するアプリケーション固有のIDを返す。 ListViewの外から「位置->固有ID」を取得できる。 アイテムを取得してアイテムからIDを取るんじゃダメなのか...。 Get the row id associated with the specified position in the list. fun getView(position:Int,convertView: View?,parent: ViewGroup?): View 指定した位置のアイテムを表示するためのビューを取得する。 convertViewには、画面から表示しきれなくなったViewが来る。 既に画面に表示されているならばnullが来る。 画面から表示しきれなくなったView(convertView)を使い回すことで負荷削減する。 Get a View that displays the data at the specified position in the data set. convertViewをArticleViewにダウンキャストした結果をViewとする。 ダウンキャストがnullならばエルビス演算子(?:)の右辺が評価され、新しいArticleViewが作られる。 View = ((convertView as? ArticleView) ?: ArticleView(context)).apply { setArticle(articles[position]) package com.example.ikuty.myapplication import android.content.Context import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import com.example.ikuty.myapplication.model.Article import com.example.ikuty.myapplication.view.ArticleView class ArticleListAdapter(private val context: Context): BaseAdapter() { var articles: List = emptyList() override fun getCount(): Int = articles.size override fun getItem(position: Int): Any? = articles[position] override fun getItemId(position: Int): Long = 0 override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View = ((convertView as? ArticleView) ?: ArticleView(context)).apply { setArticle(articles[position]) } }

default eye-catch image.

ListViewの1行を表示するViewControllerを書いてみる

ボキャブラリーが貧弱でアレなんだけども、やりたいことはそんな感じ。 リスとビューの1行に表示するアイテムのViewCOntrollerを書いてみる。 1行に前回定義したArticleを表示する予定なので、そういう文脈になっている。 findViewById()ではないKotlin風のViewの取り方 findViewById()を使うことで、IDから該当するViewを取ることができるけれど、 その応答値はnullableなので、findViewById()を使って取ったViewを使いまわそうとすると、 nullableに対するケアがつきまとうようになる。 Kotlinのlazy()を使うことでnullableでないViewの取り方を実現できる! lazy()は移譲プロパティの移譲先を書く書き方。公式の仕様はこちら。 遅延プロパティ、つまりアクセスされたときに初めて評価されるプロパティを実現する。 以下、Viewに<T: View&gt bindView(@IdRes id:Int)という拡張関数を追加している。 追加した拡張関数の戻りがnullを許容しないから、 これを経由すればnullのケアをしなくて良くなる(という話) lazy()の中で評価したfindViewById()がnullを返した場合、ダウンキャストの失敗を どう扱うんだろうか。 package com.example.ikuty.myapplication import android.support.annotation.IdRes import android.view.View fun View.bindView(@IdRes id: Int): Lazy = lazy { findViewById(id) as T } 実装 この拡張関数を使ったArticleViewは以下。 プロパティとして保持するViewがnull非許容になっている。 それにより、setArticle(artilce:Article)において、 プロパティにアクセスする際にnullのケアをしないで済んでいる。 これは便利なんじゃなかろうか。 package com.example.ikuty.myapplication.view import android.content.Context import android.graphics.Color import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import com.example.ikuty.myapplication.R import com.example.ikuty.myapplication.model.Article import com.example.ikuty.myapplication.bindView class ArticleView : FrameLayout { constructor(context: Context?) : super(context) constructor(context: Context?, attrs: AttributeSet?) : super(context,attrs) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context,attrs,defStyleAttr,defStyleRes) val profileImageView: ImageView by bindView(R.id.profile_image_view) val titleTextView: TextView by bindView(R.id.title_text_view) val userNameTextView: TextView by bindView(R.id.user_name_text_view) init { LayoutInflater.from(context).inflate(R.layout.view_article,this) } fun setArticle(article: Article) { titleTextView.text = article.title userNameTextView.text = article.user.name profileImageView.setBackgroundColor(Color.RED) } }

default eye-catch image.

Percelableを実装してIntentに乗せてBundleに渡せるモデルを書く

Percelable interface モデルをActivity間でやりとりしたり、再生成時の復元処理を書いたり、など、 Percelableというインターフェースを実装すれば、面倒みてくる。 Bundleには基本的な型以外に、Percelableを実装したインスタンスを渡すことができる。 (つまり、ActivityとFragmentに渡すことができる。) Percelableを実装するとIntentに載せることができる。 Parcelable interfaceは2つの仮想関数を持っていてそれぞれ実装する。 CREATORというstaticメンバを実装する。 abstract describeContents() abstract writeToPercel(Percel dest, int flags) CREATOR abstract describeContents() describeContents()はひとまず0を返すようにする。 0以外の応答値が必要な場合についてひとまず省略。 abstract writeToPercel(Percel dest, int flags) writeToPercel(Percel dest, int flags)はPercelに保存するデータを列挙する。 通常クラスが持つプロパティを列挙する。 CREATOR CREATORという名前のstaticフィールドを実装する必要がある。 Interface for classes whose instances can be written to and restored from a Parcel. Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface. Kotlinはstaticフィールドに対応していない(companionオブジェクトが対応する)ため、 companionオブジェクトとして実装する必要がある。 Percelable.Create<T>というobject式(シングルトンオブジェクト)を継承して作る。 Kotlinから呼ぶだけなら、companionオブジェクトを用意するだけで外からstaticメンバのように扱えるけれども、 Javaから呼ぶなら、さらに@JvmFieldを付ける必要がある。 Kotlnでプロパティを書くと、Javaに変換する際にgetXXX()やsetXXX()のように 自動的にgetter/setterが付く(らしい)。 プロパティに@JvmFieldアノテーションをつけると、getXXX()/setXXX()ではなく、 直接フィールドを触るように変換される。 @JvmFieldを付けずにstaticフィールドを書こうとすると、 勝手にgetXXX()/setXXX()が付いてしまい要求を満たせなくなる。 (JVM言語っぽいところを初めて理解した図...) 実装例は以下参照。 スコープ関数,run ここ見ながら理解。 任意の型Tの拡張関数で、そのTをレシーバとする関数Rを引数にとる。 public inline fun T.run(f: T.() -> R): R = f() 例えば、文字列\"AIUEO\"を小文字にする例。 val str = \"AIUEO\".run { toLowerCase() } println(s) // aiueo 実装例 Kotlinの文法の基本を詰めていれば問題なし。 package com.example.ikuty.myapplication.model import android.os.Parcel import android.os.Parcelable data class Article(val id: String, val title: String, val url: String, val user: User) : Parcelable { companion object { @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(source: Parcel): Article = source.run { Article(readString(),readString(),readString(), readParcelable(Article::class.java.classLoader)) } override fun newArray(size: Int): Array = arrayOfNulls(size) } } override fun describeContents(): Int = 0 override fun writeToParcel(dest: Parcel, flags: Int) { dest.run { writeString(id) writeString(title) writeString(url) writeParcelable(user, flags) } } }

default eye-catch image.

Kotlinスタートブックを読み始める

断片的な情報を集めるよりも、評判が良い良書を読む方が確実だし結果的に理解が早いと思う。 なので、超入門に続いて、Kotlin中級書として最も評判が良いだろう本を読み始めてみる。 何事もアウトプットしないと理解が定着しないので、引き続きWPへ転記。 超入門に使った書籍が薄すぎて...と思っていたのだけど、 もしかすると、さっそくKotlinの簡潔さに触れたのかもしれない。 言語仕様からくだらない拘りを捨てて、いかに現実的な書き方を実現するか、というところ。 これは良いかもしれないぞ、と。 Kotlinスタートブックposted with amazlet at 18.12.10リックテレコム (2017-03-21)売り上げランキング: 68,877Amazon.co.jpで詳細を見る 実際読んでみると、超入門で使ったKindle本で良かったかなと思う。 確かに上の方が書いてある幅が広いんだけど、下の方が要点がまとまっている気がする。 こちらの方が薄いし500円で買えるし。 速習 Kotlin: Javaより簡単!新Android開発言語を今すぐマスター 速習シリーズposted with amazlet at 18.12.10WINGSプロジェクト (2018-09-26)売り上げランキング: 1,981Amazon.co.jpで詳細を見る Amazon Cyber MondayでKindle Paper whiteを手に入れたので、 この手の本は、今後はKindleで入手する見込み。

default eye-catch image.

Kotlin@IntelliJ IDEAでAndroid hello world.

古めのIntelliJ IDEAがAndroid Studioで使われている様子で、 IntelliJ IDEAを使おうがAndroid Studioを使おうが大差ないのかもだけども、 Androidの最新の動向をトレースできるのはきっとAndroid Studio。 以下の記事を順当に流してとりあえずKotlin Android Hello World環境ができた。 IntelliJ IDEAで、JavaSDK,AndroidSDK,Android support plugin,Gradle pluginの設定をするのは順当。 参考にしたやつ。 https://qiita.com/shitake/items/150d7a94b66ba73a93ba https://qiita.com/g0z4ru/items/24bca8e6fb691f29260f 今日現在、ext.kotlin_versionがデフォの1.3.0だといろいろ見つからないと言われるので1.2.71に設定。 // build.grade // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = \'1.2.71\' // change from 1.3.0 to 1.2.7 repositories { google() jcenter() } dependencies { classpath \'com.android.tools.build:gradle:3.1.0\' classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } MainActivity.kt。ちゃんとKotlinになってる。 5年前くらいにAndroidアプリを開発していたころと同じMacで作業しているのだけども、 エミュレータが遅いのは相変わらずな以外は、意外にもモッサリしていない。 当時はEclipse、今はIntelliJ IDEA(Android Studio)。 割と2013LateのMBPで何かやろうとする気になる。 サンプルをビルドするだけでなくて、 何か簡単なアプリを1つ作ってみよう..。

default eye-catch image.

kotlin基本構文-ジェネリッック型,ジェネリック関数,共変と反変,out予約語

ジェネリック プライマリコンストラクタで受け取った文字列をプロパティとして持つオブジェクトを作成し、 その文字列を取得するコードは以下の通り。普通。 class MyObject(var value: String) { fun getProp(): String { return this.value } } val m = MyObject(\"hogehoge\") println(m.getProp()) この書き方だと、型が文字列に限定される。 型を一般化して、文法で型と紐づける書式がジェネリック。 上記のコードをジェネリックで一般化してみる。 一般化した型引数をTとしている。 class MyGenerics(var value: T) { fun getProp(): T { return this.value } } val g1 = MyGenerics(\"hogehoge\") println(g1.getProp()) // hogehoge val g2 = MyGenerics(100) println(g2.getProp()) // 100 型推論による型指定の省略 プライマリコンストラクタに渡すパラメタの型からジェネリック型が唯一決まる場合は、 インスタンス化時の型指定を省略できる。 class MyGenerics(var value: T) { fun getProp(): T { return this.value } } val g1 = MyGenerics(\"hogehoge\") println(g1.getProp()) // hogehoge val g2 = MyGenerics(\"fugafuga\") println(g2.getProp()) // fugafuga 複数のジェネリック型指定 複数のジェネリック型を指定できる。下記のような書き方になる。 class MyGenerics2(var value1: T, var value2: R) { fun getProp1(): T { return this.value1 } fun getProp2(): R { return this.value2 } } val g3 = MyGenerics2(100,\"hoge\") println(g3.getProp1()) println(g3.getProp2()) 型引数の制約 型引数TをT:typeとすると、type型かtype型の派生型という意味になり、型の制約を作れる。 ジェネリック型をHogeクラスの派生に制限する定義として、インスタンス化時にInt型を指定する例。 コンパイル時(前)にちゃんとエラーが出る。 class Hoge(var def: String) { fun getVal(): String{ return \"hoge\" } } class MyGenerics3(var value: T) { fun getProp(): T { return this.value } } // Error:(44, 32) Kotlin: The integer literal does not conform to the expected type Hoge var g4 = MyGenerics3(100) println(g4.getProp()) val hoge = Hoge(\"hoge\") var g5 = MyGenerics3(hoge) println(g5.getProp().getVal()) ジェネリック関数 関数の引数と戻り値を関数の呼び出し時に決めて紐づけられる仕組み。 ジェネリック型の定義はclassだが、ジェネリック関数はclassではなく関数。 以下は、リストの末尾(tail)を応答するジェネリック関数。 引数として配列を取るが、配列の型を呼び出し時に決められる。 fun tail(list: Array): T = list[list.size-1] var data = arrayOf(1,2,3,4,5) println(tail(data)) 共変(covariant)と不変(invariant) Javaの話。Javaでは配列の要素のクラスに継承関係があるとき、 その配列は親クラスの配列に代入できる。 aaryの定義はNumberの配列だが中身はIntegerの配列になっている。 定義上Numberの配列だから、Numberの派生クラスのインスタンスを代入する式を書いてもコンパイルが通る。 例えば、Longのインスタンスを入れられる。 実態はIntegerの配列だから、Longのインスタンスを入れた瞬間に怒られる。 Integer[] iary = new Integer[10]; Number[] aary = iary; aary[0] = new Long(0); // Exception occured. kotolinの配列は共変ではなく不変。 配列の要素に継承関係があったとしても代入できない。 これを不変(invariant)と言ってkotolinは不変が基本。 var intArray: Array = arrayOf(1,2,3) var anyArray: Array = intArray // Exception occured. ただし、意図的に共変にすることもできる。 以下の通り、kotlinの配列は不変であるから ArrayをArrayに代入できない。 コンパイル時にひっかかる。 open class Hoge(var value:String) { open fun sayHello():Unit { println(\"hoge\") } } class Fuga(value:String): Hoge(value) { final override fun sayHello(): Unit { println(\"fuga\") } } var hogeArray: Array = arrayOf(Fuga(\"hoge\"),Fuga(\"fuga\")) //Error:(17, 34) Kotlin: Type mismatch: inferred type is Array but Array was expected var fugaArray: Array = hogeArray out予約語をつけることで、その配列を共変配列にできる! open class Hoge(var value:String) { open fun sayHello():Unit { println(\"hoge\") } } class Fuga(value:String): Hoge(value) { final override fun sayHello(): Unit { println(\"fuga\") } } var hogeArray: Array = arrayOf(Fuga(\"hoge\"),Fuga(\"fuga\")) var fugaArray: Array = hogeArray 実際、コレクションのListはoutが指定してある。 なので、それを知らずともJava的に共変配列の操作のようなことができる。 interface List: Collection var list: List = listOf(\"aaa\",\"bbb\",\"ccc\") var list2: List = list

default eye-catch image.

kotlin基本構文-データクラス,シングルトンオブジェクト,オブジェクト式,SAM変換,コンパニオンオブジェクト,Enumクラス

データクラスの同値性 構造体のような用途で使うデータクラス。 プライマリコンストラクタだけで目的を達成できるからclassブロックがない。 data class Article(val url: String, val title: String) var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = Article(\"http://ikuty.com/2\",\"記事2\") インスタンスの同値性は「==」演算子で評価される。 データクラスには「equals」が定義済みで「==」評価時にequalsが呼ばれる。 データクラスのインスタンス同士を「==」演算子で比較すると、 プライマリコンストラクタで指定した値が同じ場合に限りtrueが返る。 data class Article(val url: String, val title: String) var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = Article(\"http://ikuty.com/1\",\"記事1\") var article3 = Article(\"http://ikuty.com/2\",\"記事2\") println(article1 == article2) // true println(article1 == article3) // false データクラスの同値性はプライマリコンストラクタで定義された値だけが対象となる。 データクラスに含まれる他のメンバ変数が異なっていても、プライマリコンストラクタで 定義された値が同じであればtrueを返す。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = Article(\"http://ikuty.com/1\",\"記事1\") article1.value = 100 article2.value = 100 println(article1 == article2) article1.value = 100 article2.value = 200 println(article1 == article2) データクラスのtoString インスタンスの文字列化は「toString」関数が評価される。 例えば以下のような感じ。 プライマリコンストラクタで渡さなかったメンバ変数は出てこない。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") println(article1) // Article(url=http://ikuty.com/1, title=記事1) データクラスのプロパティの分割 例えば、IntのPairを2つのIntに分割する書式は以下だった。 val pair :Pair = Pair(10,20) val (p1, p2) = pair println(p1) // 10 println(p2) // 20 この書式において、Pairクラスのcomponent2メソッドが評価されている。 一般にcomponentN関数により左オペランドのN個の変数に代入する。 Pairの場合はcomponent2が評価され、左オペランドの2個の引数に変数が代入されている。 データクラスにおいても予めcomponentNが定義されており、 左オペランドにプライマリコンストラクタで指定した値が代入される。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") val (v1, v2) = article1 println(v1) // http://ikuty.com/1 println(v2) // 記事1 データクラスの複製 copyメソッドによりインスタンスを複製できる。 その際、名前付き引数で指定した値を変更してから複製することもできる。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = article1.copy() var article3 = article1.copy(title=\"記事2\") println(article1) // Article(url=http://ikuty.com/1, title=記事1) println(article2) // Article(url=http://ikuty.com/1, title=記事1) println(article3) // Article(url=http://ikuty.com/1, title=記事2) シングルトンオブジェクト クラスではなくオブジェクトそのものを定義し、そのオブジェクトを使い回す書式。 以下のように書く。 object MySingletonSettings { var name: String = \"hoge\" } fun main(args: Array) { println(MySingletonSettings.name) } オブジェクト式 クラス定義を書かないで1回限りのオブジェクトを定義するやり方。 Androidアプリを書くときに、ボタンのイベントリスナーにオブジェクトを渡す例のアレ。 以下のように書く。 btnのsetOnClickListenerにobjectという名称のオブジェクトを渡している。 objectはView.OnClickListenerインターフェースを実装していて、 onClickメソッドをオーバーライドしている。 Androidアプリを書くとコレでいっぱいになると思われる。 btn.setOnClickListener(object: View.OnClickListener{ override fun onClick(view: View) { Log.v(\"Hello Kotlin\",\"clicked!\") } }) SAM変換 オブジェクト式で実装するインターフェースがもつ抽象メソッドが1つ (Single Abstract Method)の場合、 overrideが必要なメソッドは1つしかないということなので、 ラムダ式でそのメソッドを書くことができる。 以下のように書く。 btn.setOnClickListener({ view: View -> Log.v(\"Hello Kotlin\",\"clicked!\")}) ラムダ式のお作法として、そのラムダ式が唯一の引数である場合はブラケットの外に出せる。 そして、ブラケットを省略できる。 さらに、ラムダ式に渡る引数の型(view:View)が明らかな場合に省略できるから以下のようになる btn.setOnClickListener() { view: View -> Log.v(\"Hello Kotlin\",\"clicked!\")} btn.setOnClickListener { view: View -> Log.v(\"Hello Kotlin\",\"clicked!\")} btn.setOnClickListener { Log.v(\"Hello Kotlin\",\"clicked!\")} コンパニオンオブジェクト kotlinにはstaticメンバーは存在しない、のだけれども、 インスタンス化されていないクラス内部のコンパニオンオブジェクトのメソッドを呼び出す、 という仕組みによって、staticメンバー相当の呼び出しができる。 下記、Personクラスのプライマリコンストラクタはprivateであって外部からインスタンス化できない。 PersonクラスはFactoryオブジェクトを内包しており、Person.getInstance()というアクセスにより、 Factory.getInstance()が呼ばれ、Personインスタンスを返す。 class Person private constructor(var name: String) { companion object Factory { fun getInstance(): Person { return Person(\"パーソン\") } } override fun toString(): String { return \"My name is ${this.name}\" } } fun main(args: Array) { val p1 = Person.getInstance() println(p1) } Enumクラス 列挙型。class予約語の前にenumで置いてclassを修飾する。 列挙子は識別子で型はない。 列挙子の型チェックはis演算子で行う。 enum class Season { SPRING,SUMMER,AUTUMN,WINTER } fun main(args: Array) { val a = Season.SPRING println(a) // SPRING println(Season.SPRING == Season.SUMMER) // false println(Season.SPRING is Season) // true println(Season.SPRING is Enum) // true } 列挙型にプロパティを持たせられる。書き方が特徴的だけれども以下の通り。 列挙子の()はSeasonのプライマリコンストラクタで、 順序数(ordinal)の他に、新たに定義したvalueをもたせている。 列挙型のプライマリコンストラクタはprivateであって、外から明示的にインスタンス化できず、 列挙する時にのみ実行される。 enum class Season(val value: Int) { SPRING(1),SUMMER(2),AUTUMN(4),WINTER(8) } fun main(args: Array) { val a = Season.SUMMER println(a) // SUMMER println(a.ordinal) // 1 println(a.value) // 2 } さらにEnumクラスにメソッドを定義できる。 列挙子の列挙と、Enumクラスのメソッド定義の間がセミコロン「;」になっている。 enum class Season { SPRING { override fun getDisplayName(): String = \"春\" }, SUMMER { override fun getDisplayName(): String = \"夏\" }, AUTUMN { override fun getDisplayName(): String = \"秋\" }, WINTER { override fun getDisplayName(): String = \"冬\" }; abstract fun getDisplayName(): String } fun main(args: Array) { for (season in Season.values()) { println(season.getDisplayName()) } }