クラウドエンジニアのノート

情報技術系全般,自分用メモを公開してます。

JavaとKotlinの比較

Java vs Kotlin

参考にさせて頂いた記事

https://qiita.com/koher/items/bcc58c01c6ff2ece658f

メモ書きで残していたものなのでなぐり書きです

Javaとの違い

  • 文末の ; いらない
  • asで別名付けられる
import java.sql.Date as SqlDate
  • 変数編

  • finalな変数はval
  • 変更可能な変数は val s: String = "abc"
  • 型推論var s = "abc" と右辺値から判断
  • 型宣言に?を付けた場合は nullable (Kotlin は明示的に ? を付けないと null を代入できない)
  • 変数に付けた場合は Safe Call(安全な呼び出し)aaa?.length
  // Java
int a = 42;
final boolean b = true;
// Kotlin
var a: Int = 42
val b: Boolean = true

制御構文

  • if 基本的にjavaと同じ
  • ifが式として評価されるため,三項演算子として使える
// Java
final String foo;
if (bar < 42) {
  foo = "abc";
} else {
  foo = "xyz";
}

// Java
final String foo = bar < 42 ? "abc" : "xyz";
// Kotlin
val foo = if (bar < 42) {
  "abc"
} else {
  "xyz"
}

// Kotlin
val foo = if (bar < 42) "abc" else "xyz"
  • while 同じ
  • c++の拡張forと同じ
// Java
for (int number : numbers) {
  System.out.println(number);
}

// Java
for (int i = 0; i< 100; i++) {
  System.out.println(i);
}
// Kotlin
for (number in numbers) {
  println(number)
}

// Kotlin
for (i in 0 until 100) {
  println(i)
}
// Kotlin
for (i in 99 downTo 0) println(i) // for (int i = 99; i >= 0; i--)
for (i in 0 until 100 step 2) println(i) // for (int i = 0; i < 100; i += 2)
for (i in 1..100) println(i) // for (int i = 1; i <= 100; i++)
  • switch(){} -> when(){}
  • newは不要

class

  • kotlinはデフォルトがpublic
  • kotlinはclassのフィールドがなく,getter/setterが存在しない
    • その代わりプロパティと呼ばれるものがある

コンストラク

  • プロパティ(javaで言うfield)の宣言とコンストラクタの宣言
  • このような形をプリマリコンストラクタという
  • プロパティの初期化?宣言?以外の処理をするにはinitが必要
// Kotlin
class Person(val firstName: String, val lastName: String) {
  var age: Int

  init {
    age = 0
  }

  ...
}
  • コンストラクタを複数作るには
  • ま,この場合はデフォルトコンストラクタでいいんですけどね
// Java
public class Person {
  private final String firstName;
  private final String lastName;
  private int age;

  public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }

  public Person(String firstName, String lastName) {
    this(firstName, lastName, 0);
  }

  ...
}
// Kotlin
class Person(val firstName: String, val lastName: String, var age: Int) {
  constructor(firstName: String, lastName: String) : this(firstName, lastName, 0) {
  }

  ...
}

プロパティ?

// Java
final int age = person.getAge();
person.setAge(age + 1);

// Java
public class Person {
  ...

  public String getFullName() {
    return firstName + " " + lastName;
  }
}
// Kotlin
val age = person.age
person.age = age + 1

// Kotlin
class Person {
  ...

  val fullName: String
    get() {
      return firstName + " " + lastName
    }
}

式が一つで済むときはこれで住む

// Kotlin
  val fullName: String
    get() = firstName + " " + lastName

メソッド

  • 特に変化は無くfunが必要なだけ
  • fun 関数名(引数):戻り値
// Kotlin
class Person(val firstName: String, val lastName: String) {
  ...

  // 時間経過を表すメソッド
  fun elapse(years: Int): Int {
    age += years
    return age
  }
}
  • 式が一つで済むなら以下のようにかける
// Kotlin
fun getFullName(): String = firstName + " " + lastName

カスタムgetter/setter

  • 基本的にgetter/setterは必要ないが,何か処理したい時
    // カスタムゲッターを定義
    val nameLength: Int
        get(): Int {
            return this.name.length
        }

    // カスタムセッターを定義
    var name: String = ""
        set(value) {
            println("nameに${value}が設定されました。")
            // 値は field変数に格納する
            field = value
        }

継承

  • Kotlin ではデフォルトでクラスやメソッドは final
  • final でなくすには open のキーワードをつける
  • extendの代わりに:
  • override修飾子は必須項目
    • valvarもついていない -> 何もつけなければそれはただの引数
// Kotlin
// `open` で `final` でなくす
open class Person(val firstName: String, val lastName: String, var age: Int) {
  ...

  open val fullName: String // `final` でなくす
    get() = firstName + " " + lastName
}

class EasternPerson(firstName: String, lastName: String, age: Int) : Person(firstName, lastName, age) {
  override val fullName: String
    get() = lastName + " " + firstName // 姓 名の順にする
}

データクラスとコピー

  • データだけ保持したいクラスはdata修飾子をつける
  • 以下の要素が自動生成される
    • equals()/hashCode()ペア
    • "User(name=John, age=42)"って表示するtoString()
    • 宣言順で内容を取り出すcomponentN()関数
    • copy()
data class User(val name: String, val age: Int)
  • fun copyがデータクラスに自動生成される
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2) // nameはそのままでageだけ変更したコピーを生成

interface

// Java
public interface Foo {
  int getBar();
  void baz(String qux);
}
// Kotlin
interface Foo {
  val bar: Int
  fun baz(qux: String)
}

コレクション

// Java
final List<Integer> a = Collections.unmodifiableList(Arrays.asList(2, 3, 5));

final List<Integer> b = new ArrayList();
b.add(2);
b.add(3);
b.add(5);
// Kotlin
val a: List<Int> = listOf(2, 3, 5)

val b: MutableList<Int> = mutableListOf()
b.add(2) // 可変
b.add(3) // 可変
b.add(5) // 可変


// Kotlin
val a = mutableListOf(2, 3, 5)
val b: List<Int> = a

println(b) // [2, 3, 5]

a.add(7)

println(b) // [2, 3, 5, 7]


// Kotlin
val a = intArrayOf(2, 3, 5)

deligate(譲位)

https://developer.android.com/training/basics/fragments/communicating.html#kotlin

  • 難しいことはよくわからんが,保持されてる側のクラスから,保持してる側のクラスへ通知するしくみ
  • playGroundで試せるよ
  • 生成される側にListenerインターフェイス定義
  • callback変数を定義して,それをsetする関数用意(どこかで生成された関数オブジェクト?を格納)(本系はlateinitじゃなくても動いてた)
  • インスタンス保持側でinterfaceの実装(本家はoverrideなかった...)
// インスタンス生成される側に定義
class Fragment(){
    internal lateinit var callback: SomeListener
    
    fun setSomeListener(callback: SomeListener){
        this.callback = callback
    }
    fun sendMessage(){
      callback.messageRecv("Hello")
    }

    // This interface can be implemented by the Activity, parent Fragment,
    // or a separate test implementation.
    interface SomeListener{
        fun messageRecv(str: String)
    }
}

// 実装はインスタンスを持つ側
class Activity(): Fragment.SomeListener{
    val fragment = Fragment()
    
    //本来onFragmentAttachとか
    //だから本来この上でいちいちfragment定義せんでもよいのよな
    //fun onAttachFragment(fragment: Fragment) {
    //  if (fragment is HeadlinesFragment) {
    //      fragment.setOnHeadlineSelectedListener(this)
    //  }
    //}
    init {
      // コンストラクタで渡そう
        fragment.setSomeListener(this)
    }
    
    override fun messageRecv(str: String){
        println("getMessage!: " + str)
    }
}


fun main() {
   val activity = Activity()
   activity.fragment.sendMessage()
}

そんなsetSomeListenerのところコンストラクタで渡せばもっとシンプル!!

lazy

http://atma1333.hatenablog.com/entry/2017/08/02/124122
より

  • lazyは引数に関数を取り,この関数は対象のプロパティにセットされる値を返します。
  • 対象のプロパティが最初に参照された時lazyの引数に渡された関数が呼び出されます。
  • 2回目以降の参照では呼び出されません。
class CustomView(context: Context?) : FrameLayout(context) {

    var text1: TextView? = null
    var text2: TextView? = null
    var image: ImageView? = null

    init {
        LayoutInflater.from(context).inflate(R.layout.item_view, this)
        text1 = findViewById(R.id.text1)
        text2 = findViewById(R.id.text2)
        image = findViewById(R.id.image)
    }
}

lazyつかって書くと

class CustomView(context: Context?) : FrameLayout(context) {

    val text1: TextView by lazy { findViewById<TextView>(R.id.text1) }
    val text2: TextView by lazy { findViewById<TextView>(R.id.text2) }
    val image: ImageView by lazy { findViewById<ImageView>(R.id.image) }

    init {
        LayoutInflater.from(context).inflate(R.layout.item_view, this)
    }
}

lateinit

  • NonNullなプロパティーの初期化をconstructorより後に遅らせられる機能

as

  • キャスト 失敗でエラー
  • as? はキャストだけど失敗でnullになる

オブジェクト式

  • あるクラスの一つだけを修正(オーバーライド)したインスタンスがほしいとき
open class A(x: Int) {
  public open val y: Int = x
}

interface B {...}

val ab: A = object : A(1), B {
  override val y = 15
}

ラムダ式

  • kotlinは関数が第一級関数だから関数オブジェクトとして扱える
public class Main {

    fun main(args: Array<String>) {

        val op = { a:Int, b:Int -> a+b}
        calc(1,2,op) // 3
    }

    fun calc(a: Int, b: Int, op: (Int, Int) -> Int): Int {
        return op(a, b)
    }
}

型推論できないときは明示的に

val noReturn : Int -> Unit = { num -> println(num) }
val more : (String, Int) -> String = { str, int -> str + int }

// クラス拡張としてもラムダ式OK
val another : String.(Int) -> String = { this + it }

スコープ関数

val s = "hoge".let { it.toUpperCase() }
println(s) //=> HOGE

val s = "hoge".run { toUpperCase() }
println(s) //=> HOGE

val s = "hoge".apply { toUpperCase() }
println(s) //=> hoge

let

  • 任意の型の拡張関数
  • letは引数として関数を受け取り,任意の型で返す
    • nullableな変数に対して使うことが多い
val upperCase: String? = foo?.let { it.toUpperCase() }
// nullなら?.呼び出しによりletが実行されない
// nullじゃないなら,letが実行され大文字で帰ってくる

run

  • letとwithが合わさった感じ
  • runは任意の型Tの拡張関数でそのTをレシーバとするメソッドのような関数を引数に取る
val frame: JFrame? = frameOrNull()
frame?.run {
  size = Dimension(600, 400)
  defaultCloseOperation = JFrame.EXIT_ON_CLOSE
  setVisible(true)
}

apply

  • letは引数として関数を受け取り,レシーバを返す
val frame: JFrame = JFrame("My App").apply {
  size = Dimension(600, 400)
  defaultCloseOperation = JFrame.EXIT_ON_CLOSE
  setVisible(true)
}
class MyFragment: Fragment() {
  companion object {
    fun new(foo: Int, bar: Int): MyFragment =
      MyFragment().apply {
        arguments = Bundle().apply {
          putInt("foo", foo)
          putInt("bar", bar)
        }
      }
  }
}

Kotlin : as, !, ? 周りのチートシート

この記事が良くまとまっていて秀逸です http://increment.hatenablog.com/entry/2015/10/31/090743