はじめてのKotlin。Javaと比較してみた

プログラミング言語[Kotlin](https://kotlinlang.org/)について少し勉強したので、自分の備忘の為にもJavaとの比較付きでまとめています。
情報は適宜修正・追加する予定です。
*Kotlinのバージョンは1.1+、Javaのバージョンは7とします。

# 変数

|Kotlin|Java|
|:----:|:--:|
|var a = 123|int a = 123;|

Kotlinは型推論する言語です。明示的に型を付けるときは、

```kotlin
var a: Int = 123
```

のように変数名の後に`:型`と書きます。

# 定数

|Kotlin|Java|
|:----:|:--:|
|val NUM_MAX = 123|final int NUM_MAX = 123;|

定数も変数と同様に型を省略できます。

# Number

|Kotlin |Java  |bit|              |
|:-----:|:----:|:-:|:-------------|
|Byte   |byte  |8  |123           |
|Short  |short |16 |123           |
|Int    |int   |32 |123           |
|Long   |long  |64 |123L          |
|Float  |float |32 |123.4f, 123.4F|
|Double |double|64 |123.4         |

2進数表記: `0b00001011`
16進数表記: `0x0F`

# Boolean

|Kotlin |Java   |
|:-----:|:-----:|
|var b: Boolean = false|boolean b = false;|
|true   |true   |
|false  |false  |
|&&     |&&     |
|&#124;&#124;|&#124;&#124;|
|!      |!      |

# String

|Kotlin                   |Java                 |
|:-----------------------:|:-------------------:|
|val str: String = "Hello"|String str = "Hello";|

# ビット演算子

|Kotlin|Java|
|:----:|:--:|
|shl   |<<  |
|shr   |>>  |
|ushr  |>>> |
|and   |&   |
|or    |&#124;|
|xor   |^   |
|inv   |~   |

# 制御構文
## if-else
**Kotlin**

```kotlin
var max: Int
if (a > b)
  max = a
else
  max = b
```

**Java**

```java
int max;
if (a > b)
  max = a;
else
  max = b;
```

## 三項演算子
**Kotlin**
Kotlinには三項演算子はありません。が、if-elseを使って下記のように書けます。

```kotlin
val a = if (n > 0) x else y
```

上記は、

```kotlin
val a = if (n > 0) {
  x
} else {
  y
}
```

と書くこともできます。これはKotlinのif文は{}ブロックの最後の行の値を返す式として働くためのようです。

**Java**

```java
int a = n > 0 ? x : y;
```

## when
**Kotlin**

```kotlin
val x = 2
when (x) {
  1 -> println("one")
  2 -> println("two")
  else -> println("otherwise")
}
// => "two"

val y = 2
when (y) {
  1,2 -> println("one or two")
  else -> println("otherwise")
}
// => "one or two"
```

Javaで言うところのswitch文。この他にも[Kotlin特有の書き方があります](http://kotlinlang.org/docs/reference/control-flow.html#when-expression)。

**Java**

```java
int x = 2;
switch (x) {
  case 1:
    System.out.println("one");
    break;
  case 2:
    System.out.println("two");
    break;
  default:
    System.out.println("otherwise");
}
// => "two"

int y = 2;
switch (y) {
  case 1:
  case 2:
    System.out.println("one or two");
    break;
  default:
    System.out.println("otherwise");
}
// => "one or two"
```

## for
**Kotlin**

```kotlin
for (i in 0..5) {
    Log.d("TEST", "$i")
}

// => 0
// => 1
// => 2
// => 3
// => 4
// => 5
```

**Java**

```java
for (int i = 0; i <= 5; ++i) {
    Log.d("TEST", "" + i);
}

// => 0
// => 1
// => 2
// => 3
// => 4
// => 5
```

**Kotlin**

デクリメントの場合は`downTo`というちょっと馴染みのないキーワードを使います。

```kotlin
for (i in 5 downTo 0) {
    Log.d("TEST", "$i")
}

// => 5
// => 4
// => 3
// => 2
// => 1
// => 0
```

**Java**

```java
for (int i = 5; i >= 0; --i) {
    Log.d("TEST", "" + i);
}

// => 5
// => 4
// => 3
// => 2
// => 1
// => 0
```

# 関数
**Kotlin**

```kotlin
fun add(a: Int, b: Int): Int {
  return a + b
}
```

**Java**

```java
int add(int a, int b) {
  return a + b;
}
```

# クラス
**Kotlin**

```kotlin
class User {
  ...
}
```

**Java**

```java
class User {
  ...
}
```

# コンストラクタ
**Kotlin**

```kotlin
class User(name: String) {
  private val name = name
}
```

上記は以下のように書いても同義です。

```kotlin
class User(private val name: String) {
}
```

**Java**

```java
class User {
  private String name;
  public User(String name) {
    this.name = name;
  }
}
```

上記の例はメンバ変数に値をセットするだけでしたが、コンストラクタ内で何か処理もしたい時は`init`を使います。

**Kotlin**

```kotlin
class User(private val firstName: String, private val lastName: String) {

    private val fullName: String
    
    init {
        this.fullName = StringBuilder()
            .append(firstName)
            .append(" ")
            .append(lastName)
            .toString()
    }
}
```

この場合は更に以下のように簡潔に書けます。

```kotlin
class User(private val firstName: String, private val lastName: String) {

    private val fullName: String = StringBuilder()
            .append(firstName)
            .append(" ")
            .append(lastName)
            .toString()
}
```

**Java**

```java
class User {

    private String firstName;
    private String lastName;
    private String fullName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.fullName = new StringBuilder()
            .append(firstName)
            .append(" ")
            .append(lastName)
            .toString();
    }
}
```

## セカンダリコンストラクタ
2つ以上のコンストラクタを持つ場合は下記のように書けます。

**Kotlin**

```kotlin
class User(private val name: String) {
  private var age:Int = 0
  constructor(name:String, age:Int) : this(name) {
    this.age = age
  }
}
```

**Java**

```java
class User {
  private String name;
  private int age;
  public User(String name) {
    this.name = name;
  }
  public User(String name, int age) {
    this(name);
    this.age = age;
  }
}
```

# 抽象クラス
**Kotlin**

```kotlin
abstract class AbsPerson {
  abstract fun setName(name: String)
}

class Person : AbsPerson() {
  override fun setName(name: String) {
    ...
  }
}
```

**Java**

```java
abstract class AbsPerson {
  abstract public void setName(String name);
}

class Person extends AbsPerson {
  public Person() {
    super();
  }

  @Override
  public void setName(String name) {
    ...
  }
}
```

# 継承
**Kotlin**

```kotlin
open class A {
  open fun v() {}
}

class B() : A() {
  override fun v() {}
}
```

Kotlinではデフォルトで`final`クラス扱いになるようで、継承を可能にするためには`open`キーワードを付ける必要があります。
また、メソッドをオーバーライド可能にするためにも`open`キーワードが必要です。

**Java**

```java
class A {
  void v() {}
}

class B extends A {
  @Override
  void v() {}
}
```

# インタフェース
**Kotlin**

```kotlin
interface IChangeListener {
    fun onChanged(newValue: Int)
}
```

**Java**

```java
interface IChangeListener {
    void onChanged(int newValue);
}
```

# 無名クラス (匿名クラス)
**Kotlin**

```kotlin
val hoge = Hoge()
hoge.setOnChangeListener(object : IChangeListener {

    override fun onChanged(newValue: Int) {
        // Something to do
    }
})
```

```kotlin
class Hoge {

    private var listener: IChangeListener? = null

    fun setOnChangeListener(l: IChangeListener?) {
        listener = l
    }
}
```

**Java**

```java
Hoge hoge = new Hoge();
hoge.setOnChangeListener(new IChangeListener() {

    @Override
    public void onChanged(int newValue) {
        // Something to do
    }
});
```

```java
class Hoge {

    private IChangeListener listener = null;

    public void setOnChangeListener(IChangeListener l) {
        listener = l;
    }
}
```

# アクセス修飾子

|Kotlin   |Java     |相違点|
|:-------:|:-------:|:----|
|public   |public   |-    |
|protected|protected|Kotlin: 継承していないと同一package内でも参照できない。<br>Java: 継承していなくても、同一packageなら参照可能。|
|private  |private  |Kotlin: インナークラスのprivate変数が参照できない。<br>java: インナークラスのprivate変数も参照できる。|
|internal |-        |Kotlin: パッケージが違っても同じモジュール内なら参照可能。
|アクセス修飾子を省略|アクセス修飾子を省略|Kotlin: publicと同じ。<br>Java: 同じパッケージ内なら参照可能。|

Kotlinにおけるモジュールとは[公式サイト](https://kotlinlang.org/docs/reference/visibility-modifiers.html#modules)から引用すると下記の通りです。
> The internal visibility modifier means that the member is visible with the same module. More specifically, a module is a set of Kotlin files compiled together:

> - an IntelliJ IDEA module;
> - a Maven or Gradle project;
> - a set of files compiled with one invocation of the Ant task.

# クラスメソッド
**Kotlin**
Kotlinではstaticメソッドはありません。[Companion Objects](https://kotlinlang.org/docs/reference/classes.html#companion-objects)という仕組みを使えば実現できますが、公式では下記のようにpackage-level functionで書くことを推奨しています。

```kotlin
package hoge.foo

// package-level function
fun doA() {
  ...
}
```

```kotlin
import hoge.foo.doA

class B {
  fun doB() {
    doA()
  }
}
```

*staticメンバ変数も同様にpackage-levelに書くかCompanion Objectsを使います。

**Java**

```java
package hoge.foo;

class A {
 public static doA() {
    ...
  }
}
```

```java
import hoge.foo.A;

class B {
  public void doB() {
    A.doA();
  }
}
```

## Companion Objects
Companion Objectsは下記例のように`companion object {}`でメソッドや変数・定数を囲みます。

**Kotlin**

```kotlin
class Settings private constructor() {

    var useTutorial = false

    companion object {

        private const val KEY_USE_TUTORIAL = "com.your.domain.USE_TUTORIAL"

        private val instance = Settings()

        fun getInstance(context: Context): Settings {
            val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
            instance.useTutorial = sharedPref.getBoolean(KEY_USE_TUTORIAL, true)
            return instance
        }
    }
}
```

```kotlin
// 利用例
if (Settings.getInstance(context).useTutorial) {
    ...
}
```

**Java**

```java
final class Settings {

    private static final boolean KEY_USE_TUTORIAL = "com.your.domain.USE_TUTORIAL";

    private static Settings instance = new Settings();

    public boolean useTutorial = false;

    private Settings() {
    }

    public static Settings getInstance(Context context) {
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
        instance.useTutorial = sharedPref.getBoolean(KEY_USE_TUTORIAL, true);
        return instance;
    }
}
```

```java
// 利用例
if (Settings.getInstance(context).useTutorial) {
    ...
}
```

# 列挙型（Enum)
**Kotlin**

```kotlin
enum class MessageLevel {
  DEBUG, INFO, WARNING, ERROR
}
```

**Java**

```java
enum MessageLevel {
  DEBUG, INFO, WARNING, ERROR
}
```

# Additional
ここからは、アレってKotlinではどうやって書くんだ? というTips的なものを書いていきます。

## Class.class
**Kotlin**

```kotlin
MainActivity::class.java
```

**Java**

```java
MainActivity.class
```

## Activity.this
**Kotlin**

```kotlin
this@MainActivity
```

**Java**

```java
MainActivity.this
```

## BufferedReader
BufferedReaderクラスを使ってテキストを読み込む例です。
Kotlinでは`while ((line = reader.readLine()) != null)`のような記述ができないため、以下のようにすると良いです。

**Kotlin**

```kotlin
val reader = BufferedReader(InputStreamReader(inputStream))
val builder = StringBuilder()

try {
    reader.readLines().forEach { builder.append(it) }

    val str = builder.toString()
}
catch (e: IOException) {

}
```

**Java**

```java
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder = new StringBuilder();

try {
    String line;
    while ((line = reader.readLine()) != null) {
        builder.append(line);
    }

    String str = builder.toString();
}
catch (IOException e) {

}
```