Kotlinのリファレンスを読んでいる
kotlinlang.org
諸事情でKotlinを覚えるかとなったので
とりあえずこれをざっと読んだけど結局リファレンスを読んでいる
この本はJavaわかれば電車とかでびゃっと読めてなんとなくKotlinかけるようになると思うのでやる気あんまないけどざっくり把握したいみたいな時には良さそう
try式ないとかあったのでなんとなく書くレベルで必要なのが網羅できてるかというとなんとも言えない
やる気があるならリファレンス読めば良さそう
だいたいのことがそう
どこまで読んだか忘れちゃうのでメモレベルのメモ
- ifが式なので三項演算子ない
- if式はelseないとだめ
- when(switchみたいの)もそう
- 引数なしwhenはif-else ifのノリで使える
- ラムダ式のなかでreturnするとラムダ式を書いた関数からreturnされちゃう
- ラムダ式だけをreturnしたい場合はラベルで頑張る
- 匿名関数で書けば匿名関数だけreturnできる
- ここはちょっと微妙な気持ちなったけどラベルガンガン使ってく文化なのかな
- プライマリコンストラクタはbodyを持てないので、initブロックで初期化処理を書く
- initブロックは複数かける。書いた順に実行される
- initブロックではプライマリコンストラクタの引数を参照できる
- プライマリコンストラクタの引数はクラスのフィールドになる
- valで宣言した引数はread-onlyなフィールドになる
fun main() {
var test = Test("test")
println(test.value)
}
class Test(var value:String){
init {
println("You can read property defining at Primary Constructor. $value")
}
init {
println("You can define multiple initializer")
}
}
- 全てのinitializerはセカンダリコンストラクタよりも前に実行される
- プライマリコンストラクタがないとデフォルトコンストラクタを生成される。デフォルトコンストラクタを露出せずプライマリコンストラクタを定義したくない場合、引数なしでprivateなプライマリコンストラクタを定義する
- プライマリコンストラクタの引数が全てデフォルトを持っていると、JVM上ではそのデフォルトを使う引数なしコンストラクタを生成する
- 全てのクラスはAnyを継承している
- 継承する場合は継承元のコンストラクタで初期化してあげること
open class Super(val a:Int, val b:Int)
class Child1(a2:Int, b2:Int): Super(a2,b2)
class Child2: Super {
constructor():super(1,2)
}
- オーバーライドできる要素にはopenを明示する必要がある
- overrideしたメソッドを、さらに継承したクラスでoverrideするのを防ぐ場合はfinal
open class Super {
open fun method() {
println("parent")
}
}
open class Child1 :Super(){
override fun method() {
super.method()
println("child1")
}
}
class Child2 :Child1(){
override fun method() {
super.method()
println("child2")
}
}
fun main(args: Array<String>) {
Child2().method()
}
- プロパティもオーバーライドできる
- あんまり用途がわかってない
- カスタムアクセサの拡張とかだろうか🍙
- 派生クラスの初期化よりもベースクラスの初期化のが先に走る
- ベースクラスのinitializerでopenなメンバを使わない
- 派生クラスでoverrideしたメンバが初期化されてないのにinitializerで呼ばれる形になるので、予期しない動きをする可能性がある
- ベースクラスが複数あって同じメンバが存在したらオーバーライド必須
- クラスはstaticなメソッドを持てない
- トップレベル関数を定義しよう
- クラス経由で関数を呼びたいが、初期化はしない場合はオブジェクト宣言orコンパニオンオブジェクト
- backing fieldの参照はカスタムアクセサを介さない
- カスタムアクセサの中でフィールドを参照すると再帰になって死ぬ
- backing propertiesの用途がよくわからない
- lateinitをつけた変数はインスタンス生成時に初期化しなくてもコンパイルエラーにならない
- ただし、初期化せずにコールすると例外
- DIとかの文脈で使うらしい
- Setterインジェクションとか?
- Nullableでnullで初期化するより取り回しいいのかな?
- インターフェースはプロパティをもてる
- が、abstructかカスタムアクセサの実装のみ
- backing fieldはもてないし、カスタムアクセサから触れない
- 複数のインターフェースを実装した時にシグネチャが被るメンバがあったらそいつはオーバーライドしないといけない
- トップレベルに関数を定義できる
- トップレベルの要素に対するVisibility Modifiers
- privateはfile内から参照、internalはモジュール内から参照できる。protectedはつけられない
- クラスのメンバに対するVisibility Modifiers
- privateはクラス内、protectedはそのクラスとサブクラスから、internalはモジュール内から参照
- モジュールはまとめてコンパイルした組み合わせのこと?🍙
fun String.yeah(){
println(this)
println("yeah")
}
fun main(args: Array<String>) {
"oh".yeah()
}
- メンバと拡張関数のシグネチャが同じだとメンバが優先される
- Nullable型を拡張するとレシーバがNullableになる
- 拡張プロパティ
- カスタムアクセサだけ
- プロパティとかフィールドとかの言葉の定義を一回整理したい🍙
- クラスのなかで他のクラスを拡張できる
class C (val value:Int)
class D {
fun C.a(){
println(this.value)
}
fun b(c:C){
c.a()
}
}
fun main(args: Array<String>) {
D().b(C(1))
}
- Lombokの@Dataみたいな
- 生成されたメソッドはprimary constructorで定義したメンバしか使わない
- 型パラメータが推論できる場合は、コンストラクタ呼び出し時に省略できる
class A(val value:T)
class B()
fun main(args: Array) {
var a = A(1)
var b = B() // コンパイルエラー
}
- in/outはこの記事がわかりやすかった
- inは型パラメータが引数の型にだけ使われるマーク
- だったら、Tが引数の型のメソッドがある
- その型パラメータが設定されたクラスのメソッドは、Tの継承クラスを渡せるはず
- outは型パラメータが戻り値の型にだけ使われるマーク
- ちょっとなんか怪しいのであとでもう一度🍙
- ネストされたクラスにinnerがついてるとouterを参照できる
class A (val value:Int){
inner class B {
fun print() {
print(value)
}
}
}
interface A {
fun print()
}
abstract class B {
abstract fun print()
}
fun main(args: Array<String>) {
val a = object : A {
override fun print() {
print("test")
}
}
a.print()
val b = object : B() {
override fun print() {
print("test")
}
}
b.print()
}
- Sam変換微妙に理解できてない🍙
- Javaの関数型インターフェースにのみ使えるっぽい
fun test(s:Comparator<Int>) {
s.compare(1,2)
}
fun main(args: Array<String>) {
test(Comparator { o1, o2 -> o2 - o1 })
test({ o1, o2 -> o2 - o1 })
}
enum class Test {
A {
override fun print() {
println("AAAA")
}
},
B {
override fun print() {
println("BBBB")
}
};
abstract fun print()
}
fun main(args: Array<String>) {
Test.A.print()
Test.B.print()
}
- interfaceを実装できる
- メンバはclassに定義してもいいし、enum値に定義してもいい
interface A {
fun A()
}
interface B {
fun B()
}
enum class E:A,B {
ENUM{
override fun B() {
println("BBBB")
}
};
override fun A() {
println("AAAA")
}
}
fun main(args: Array<String>) {
E.ENUM.A()
E.ENUM.B()
}
- なんかの方を継承したanonymous classのインスタンス生成には、object式を使う
- object式に複数の型を渡す奴が何なのかよくわからん🍙
- 匿名オブジェクトをpublicな関数で返すと、そのオブジェクトのメンバにはアクセスできない
- クラスっぽい感じでobjectを宣言するとシングルトンになる
- スレッドセーフ
- 初期化はスレッドセーフ
- 何かのクラスを継承できる
- クラス内のオブジェクトはcompanionで宣言できる
- コンパニオンオブジェクトがあんまり理解できてない?🍙
typealias Maaaap = Map<String, Map<String, String>>
fun main(args: Array<String>) {
val map : Maaaap = mapOf("p" to mapOf("c" to "v"))
}
typealias F = (Int) -> Int
fun main(args: Array<String>) {
var a:F = {a:Int -> a}
}
- ネストされたクラスにも行ける
- インナークラスとネストされたクラスの違いが理解できてない🍙
- あくまでエイリアスで同様の型として扱われる
typealias Integer = String
fun method(value:String) {
print(value)
}
fun main(args: Array<String>) {
val ok:Integer = "YEAH"
method(ok)
}
class Yes {
fun yes(){
println("yes")
}
}
inline class LoggingYes(val value: Yes) {
fun yes(){
log.info("--- yes start ---")
value.yes()
log.info("--- yes end ---")
}
}
- byに指定したプロパティの引数をオブジェクトでインターナルに保存し、そのクラスが持っている全てのメンバをbyが指定されたクラスのメンバとして扱える
- カスタムアクセサの共通化のためにgetterとsetterを定義したクラスを作ってbyで委譲できる
- lazyにLambdaを渡すと遅延評価できる
import kotlin.reflect.KProperty
class Data {
var count:Int = 0
val a:String by lazy {
println("compute")
count++
"ohhhhhhhhhhhhhhhhhhhh"
}
}
fun main(args: Array<String>) {
val e = Data()
println(e.count)
println(e.a)
println(e.count)
println(e.a)
println(e.count)
}
- Delegates.observableはsetterの処理を委譲
import kotlin.properties.Delegates
class Data {
var a:String by Delegates.observable("initial"){
property, oldValue, newValue ->
println("$property from $oldValue to $newValue")
}
}
fun main(args: Array<String>) {
val e = Data()
e.a = "a"
}
- vetoableはbooleanを返すラムダを定義してfalseを返すと値を更新しない
import kotlin.properties.Delegates
class Data {
var a:String by Delegates.vetoable("initial"){
property, oldValue, newValue ->
println("$property from $oldValue to $newValue")
if (newValue == "fuck") {
false
} else {
true
}
}
}
fun main(args: Array<String>) {
val e = Data()
e.a = "a"
println(e.a)
e.a = "fuck"
println(e.a)
}
- なぜmapに委譲できるのかよくわからない 🍙
- local変数も委譲プロパティ使える
- provideDelegate理解できてない 🍙
open class Yes {
open fun hello(value:String = "hello") {
println(value)
}
}
class Yes2: Yes() {
override fun hello(value: String) {
super.hello(value)
println("hello2")
}
}
fun main(args: Array<String>) {
Yes2().hello();
}
- 戻り値が不要な場合、戻り値はUnitとなる
- 1つの式だけの関数は中括弧を省略できる
fun a(a:Int, b:Int) = a * b
fun main(args: Array<String>) {
println(a(2, 3))
}
- 戻り値の型は推論されない
- 可変長引数はvararg
- infix function
infix fun String.join(value:String) = this + value
fun main(args: Array<String>) {
println("YEAH" join "OHHHHHHHHHH")
}
fun main(args: Array<String>) {
fun yes() {
println("yes")
}
yes()
}
- 関数型は関数型インターフェースじゃなくてこんな感じ
val sum1 = { x: Int, y: Int -> x + y }
val sum2:(Int, Int) -> Int = { x,y -> x + y }
fun main(args: Array<String>) {
println(sum1.invoke(1, 2))
println(sum2.invoke(1, 2))
}
fun test(text:String, func: (String) -> String){
println(text)
println(func(text))
}
fun main(args: Array<String>) {
test("yes", {it + "func"})
test("yes"){it + "func"}
}
fun test(text:String, func: (String) -> String){
println(text)
println(func(text))
}
fun main(args: Array<String>) {
var a = fun(a:String): String {
return a + "func";
}
test("yes", a)
}
* 外側のスコープの変数にアクセスできるし、変更もできる
fun main(args: Array<String>) {
var a = 1
var b = {println("b");a = 100}
println(a)
b()
println(a)
}
fun main(args: Array<String>) {
val join = fun String.(arg:String): String = this + arg
println("yes".join(" no"))
}