なぜKotlinなのですか?Java開発者に切り替えを説得できる8つの機能

2016年に正式にリリースされたKotlinは、特にGoogleがAndroidプラットフォーム上のJavaの代替としてKotlinのサポートを発表して以来、近年多くの注目を集めています。KotlinをAndroidの優先言語にするという最近発表された決定により、新しいプログラミング言語の学習を開始する時期かどうか疑問に思われるかもしれません。その場合は、この記事が決定に役立つ可能性があります。

Kotlinのリリース履歴

Kotlinは2011年に発表されましたが、最初の安定版リリースであるバージョン1.0は2016年まで登場しませんでした。この言語は無料でオープンソースであり、AndreyBreslavが主要言語デザイナーとしてJetBrainsによって開発されました。Kotlin1.3.40は2019年6月にリリースされました。

Kotlinについて

Kotlinは、オブジェクト指向プログラミング構造と関数型プログラミング構造の両方を備えた、静的に型指定された最新のプログラミング言語です。これは、JVMを含むいくつかのプラットフォームを対象としており、Javaと完全に相互運用可能です。多くの点で、Kotlinは、Javaが今日設計された場合にどのように見えるかを示しています。この記事では、Java開発者が発見することに興奮すると信じているKotlinの8つの機能を紹介します。

  1. クリーンでコンパクトな構文
  2. シングルタイプシステム(ほぼ)
  3. ヌルの安全性
  4. 関数と関数型プログラミング
  5. データクラス
  6. 拡張機能
  7. 演算子のオーバーロード
  8. トップレベルオブジェクトとシングルトンパターン

こんにちは世界!KotlinとJava

リスト1は、必須の「Hello、world!」を示しています。Kotlinで記述された関数。

リスト1.「Hello、world!」Kotlinで

 fun main() { println("Hello, world!") } 

この例は、単純ですが、Javaとの主な違いを示しています。

  1. mainトップレベルの関数です。つまり、Kotlin関数をクラス内にネストする必要はありません。
  2. public static修飾子はありません。Kotlinには可視性修飾子がありますが、デフォルトはpublic省略できます。Kotlinもstatic修飾子をサポートしていませんmainが、トップレベルの関数であるため、この場合は必要ありません。
  3. Kotlin 1.3以降、の文字列配列パラメーターmainは必須ではなく、使用しない場合は省略できます。必要に応じて、として宣言されargs : Arrayます。
  4. 関数の戻り値の型は指定されていません。Javaがを使用するvoid場合、Kotlinはを使用しUnit、関数の戻り値の型がである場合はUnit省略できます。
  5. この関数にはセミコロンはありません。Kotlinでは、セミコロンはオプションであるため、改行は重要です。

これは概要ですが、KotlinがJavaとどのように異なり、多くの場合、Javaを改善するかについて学ぶことはまだたくさんあります。

1.よりクリーンでコンパクトな構文

Javaは冗長すぎると批判されることがよくありますが、特にソースコードをより理解しやすくする場合は、ある程度の冗長性が友だちになる可能性があります。言語設計の課題は、明快さを保ちながら冗長性を減らすことであり、Kotlinはこの課題に対処するのに大いに役立つと思います。

リスト1で見たように、Kotlinはセミコロンを必要とせず、Unit関数の戻り値の型を省略できます。KotlinをJavaのよりクリーンでコンパクトな代替手段にするのに役立つ他のいくつかの機能について考えてみましょう。

型推論

Kotlinでは、変数をとして宣言するvar x : Int = 5か、より短いが明確なバージョンを使用することができますvar x = 5。(Javaは現在var宣言をサポートしていますが、その機能はKotlinに登場してからずっと後のJava 10まで登場しませんでした。)

Kotlinにはval、読み取り専用変数の宣言もあります。これは、として宣言されているJava変数に類似していfinalます。つまり、変数を再割り当てすることはできません。リスト2に例を示します。

リスト2.Kotlinの読み取り専用変数

 val x = 5 ... x = 6 // ERROR: WILL NOT COMPILE 

プロパティとフィールド

Javaにフィールドがある場合、Kotlinにはプロパティがあります。プロパティは、Javaのパブリックフィールドと同様の方法で宣言およびアクセスされますが、Kotlinは、プロパティのアクセサー/ミューテーター関数のデフォルトの実装を提供します。すなわち、Kotlinが提供get()する機能をval特性との両方get()set()する機能var的特性。のカスタマイズされたバージョンget()set()は、必要に応じて実装できます。

Kotlinのほとんどのプロパティにはバッキングフィールドがありますが、計算されたプロパティを定義することは可能get()です。これは基本的にバッキングフィールドのない関数です。たとえば、人を表すクラスには、のプロパティdateOfBirthとの計算されたプロパティがある場合がありageます。

デフォルトと明示的なインポート

Javaはパッケージjava.langで定義されたクラスを暗黙的にインポートしますが、他のすべてのクラスは明示的にインポートする必要があります。その結果、多くのJavaソースファイルはjava.util、からコレクションクラスをインポートし、からI / Oクラスをインポートすることから始まりますjava.io。デフォルトでは、Kotlin暗黙的に輸入kotlin.*するJavaのインポートとほぼ類似している、java.lang.*しかし、Kotlinも輸入kotlin.io.*kotlin.collections.*いくつかの他のパッケージからとクラス。このため、Kotlinソースファイルは通常、Javaソースファイルよりも明示的なインポートが少なくて済みます。特に、コレクションや標準I / Oを使用するクラスの場合はそうです。

コンストラクターの「new」の呼び出しはありません

Kotlinでは、new新しいオブジェクトを作成するためにキーワードは必要ありません。コンストラクターを呼び出すには、クラス名を括弧で囲んで使用します。Javaコード

 Student s = new Student(...); // or var s = new Student(...); 

Kotlinでは次のように書くことができます。

 var s = Student(...) 

文字列テンプレート

文字列には、文字列に挿入された結果で評価されるであるテンプレート式を含めることができます。テンプレート式はドル記号($)で始まり、単純な名前または中括弧で囲まれた任意の式で構成されます。文字列テンプレートは、明示的な文字列連結の必要性を減らすことにより、文字列式を短縮できます。例として、次のJavaコード

 println("Name: " + name + ", Department: " + dept); 

短いが同等のKotlinコードに置き換えることができます。

 println("Name: $name, Department: $dept") 

拡張および実装

Javaプログラマーは、クラスがextend別のクラスとimplement1つ以上のインターフェースを使用できることを知っています。Kotlinでは、これら2つの類似した概念の間に構文上の違いはありません。Kotlinは両方にコロンを使用します。たとえば、Javaコード

 public class Student extends Person implements Comparable 

would be written more simply in Kotlin as follows:

 class Student : Person, Comparable 

No checked exceptions

Kotlin supports exceptions in a manner similar to Java with one big difference–Kotlin does not have checked exceptions. While they were well intentioned, Java's checked exceptions have been widely criticized. You can still throw and catch exceptions, but the Kotlin compiler does not force you to catch any of them.

Destructuring

Think of destructuring as a simple way of breaking up an object into its constituent parts. A destructuring declaration creates multiple variables at once. Listing 3 below provides a couple of examples. For the first example, assume that variable student is an instance of class Student, which is defined in Listing 12 below. The second example is taken directly from the Kotlin documentation.

Listing 3. Destructuring examples

 val (_, lName, fName) = student // extract first and last name from student object // underscore means we don't need student.id for ((key, value) in map) { // do something with the key and the value } 

'if' statements and expressions

In Kotlin, if can be used for control flow as with Java, but it can also be used as an expression. Java's cryptic ternary operator (?:) is replaced by the clearer but somewhat longer if expression. For example, the Java code

 double max = x >= y ? x : y 

would be written in Kotlin as follows:

val max = if (x >= y) then x else y 

Kotlin is slightly more verbose than Java in this instance, but the syntax is arguably more readable.

'when' replaces 'switch'

My least favorite control structure in C-like languages is the switch statement. Kotlin replaces the switch statement with a when statement. Listing 4 is taken straight from the Kotlin documentation. Notice that break statements are not required, and you can easily include ranges of values.

Listing 4. A 'when' statement in Kotlin

 when (x) { in 1..10 -> print("x is in the range") in validNumbers -> print("x is valid") !in 10..20 -> print("x is outside the range") else -> print("none of the above") } 

Try rewriting Listing 4 as a traditional C/Java switch statement, and you will get an idea of how much better off we are with Kotlin's when statement. Also, similar to if, when can be used as an expression. In that case, the value of the satisfied branch becomes the value of the overall expression.

Switch expressions in Java

Java 12 introduced switch expressions. Similar to Kotlin's when, Java's switch expressions do not require break statements, and they can be used as statements or expressions. See "Loop, switch, or take a break? Deciding and iterating with statements" for more about switch expressions in Java.

2. Single type system (almost)

Java has two separate type systems, primitive types and reference types (a.k.a., objects). There are many reasons why Java includes two separate type systems. Actually that's not true. As outlined in my article A case for keeping primitives in Java, there is really only one reason for primitive types--performance. Similar to Scala, Kotlin has only one type system, in that there is essentially no distinction between primitive types and reference types in Kotlin. Kotlin uses primitive types when possible but will use objects if necessary.

So why the caveat of "almost"? Because Kotlin also has specialized classes to represent arrays of primitive types without the autoboxing overhead: IntArray, DoubleArray, and so forth. On the JVM, DoubleArray is implemented as double[]. Does using DoubleArray really make a difference? Let's see.

Benchmark 1: Matrix multiplication

In making the case for Java primitives, I showed several benchmark results comparing Java primitives, Java wrapper classes, and similar code in other languages. One of the benchmarks was simple matrix multiplication. To compare Kotlin performance to Java, I created two matrix multiplication implementations for Kotlin, one using Array and one using Array . Listing 5 shows the Kotlin implementation using Array.

Listing 5. Matrix multiplication in Kotlin

 fun multiply(a : Array, b : Array) : Array { if (!checkArgs(a, b)) throw Exception("Matrices are not compatible for multiplication") val nRows = a.size val nCols = b[0].size val result = Array(nRows, {_ -> DoubleArray(nCols, {_ -> 0.0})}) for (rowNum in 0 until nRows) { for (colNum in 0 until nCols) { var sum = 0.0 for (i in 0 until a[0].size) sum += a[rowNum][i]*b[i][colNum] result[rowNum][colNum] = sum } } return result } 

次に、2つのKotlinバージョンのパフォーマンスをJavawithdoubleおよびJavawithのパフォーマンスと比較Doubleし、現在のラップトップで4つのベンチマークすべてを実行しました。各ベンチマークの実行には少量の「ノイズ」があるため、すべてのバージョンを3回実行し、結果を平均しました。これを表1に要約します。

表1.行列乗算ベンチマークの実行時パフォーマンス

時限結果(秒単位)
Java

double

Java

Double

Kotlin

DoubleArray

Kotlin

Array

7.30 29.83 6.81 15.82

I was somewhat surprised by these results, and I draw two takeaways. First, Kotlin performance using DoubleArray is clearly superior to Kotlin performance using Array, which is clearly superior to that of Java using the wrapper class Double. And second, Kotlin performance using DoubleArray is comparable to--and in this example slightly better than--Java performance using the primitive type double.

Clearly Kotlin has done a great job of optimizing away the need for separate type systems--with the exception of the need to use classes like DoubleArray instead of Array.

Benchmark 2: SciMark 2.0

My article on primitives also included a second, more scientific benchmark known as SciMark 2.0, which is a Java benchmark for scientific and numerical computing available from the National Institute of Standards and Technology (NIST). The SciMark benchmark measures performance of several computational routines and reports a composite score in approximate Mflops (millions of floating point operations per second). Thus, larger numbers are better for this benchmark.

IntelliJ IDEAの助けを借りて、JavaバージョンのSciMarkベンチマークをKotlinに変換しました。IntelliJ IDEAは、自動的に変換double[]し、int[]JavaでにDoubleArrayし、IntArrayKotlinに。私は、使用してKotlinバージョンにプリミティブを使用してJavaのバージョンを比較DoubleArrayしてIntArray。以前と同様に、両方のバージョンを3回実行し、結果を平均しました。これを表2に要約します。ここでも、表はほぼ同等の結果を示しています。

表2.SciMarkベンチマークのランタイムパフォーマンス

パフォーマンス(Mflops単位)
Java Kotlin
1818.22 1815.78