4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

私がJavaプロジェクトが嫌いな7つの理由

Last updated at Posted at 2024-11-07

Java vs Kotlin: 7つの違いとコード例、比較表

JavaとKotlinの違いを比較しながら、Javaにおける課題とKotlinが提供する解決策について解説します。


1. 冗長なコード

Javaではシンプルなモデルクラスを作成するだけでも多くのボイラープレートが必要です。Kotlinはdata classによって、ボイラープレートなしでequalshashCodetoStringなどが自動生成されます。

コード例

Java:

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public boolean equals(Object o) { /* 省略 */ }
    @Override
    public int hashCode() { /* 省略 */ }
    @Override
    public String toString() { /* 省略 */ }
}

Kotlin:

data class User(val name: String, val age: Int)

2. Null安全の欠如

JavaではNullPointerExceptionが多発しやすく、開発者が手動でnullチェックを行う必要があります。KotlinではNullable型を区別するため、nullチェックの煩わしさが解消されます。

コード例

Java:

public String getUserName(User user) {
    if (user == null) {
        return "Unknown";
    }
    return user.getName();
}

Kotlin:

fun getUserName(user: User?): String {
    return user?.name ?: "Unknown"
}

3. ラムダ式と関数型プログラミングの使いにくさ

Java 8からラムダ式が導入されましたが、Kotlinと比べると関数型プログラミングのサポートは不十分です。Kotlinはmapfilterなど豊富なメソッドを提供し、コレクション操作も簡潔に行えます。

コード例

Java:

List<String> names = users.stream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .collect(Collectors.toList());

Kotlin:

val names = users.filter { it.age > 18 }.map { it.name }

4. 古いバージョンとの互換性問題

Javaは後方互換性が重視されるため、企業によってはJava 8などの古いバージョンを使用し続けることが多く、最新機能にすぐアクセスできません。Kotlinは新しい言語であり、モダンな機能を取り入れやすいです。

JavaとKotlinのモダンな機能を、各言語バージョンで導入された特徴とともに比較します。Javaは後方互換性を重視しているため、新しい機能が登場するまでに時間がかかる傾向があり、企業によっては古いバージョンが使われ続けています。一方、Kotlinはモダンな機能を初期から多く取り入れており、アップデートも比較的早く行われています。


比較: JavaとKotlinのモダンな機能

機能 Kotlin Java
ヒアドキュメント Kotlin 1.0からサポート Java 13(Text Blocks)で導入
Record型 Kotlinのdata classで代替 Java 16で導入
Pattern Matching Kotlin 1.0からwhenを利用 Java 17以降で段階的に導入中
デフォルト引数 Kotlin 1.0からサポート Javaでは未サポート、オーバーロードで代替
シングルトン Kotlin 1.0からobjectでサポート Javaは標準ライブラリでサポートされておらず、通常はenumを利用

1. ヒアドキュメント

JavaではJava 13からText Blocksとしてヒアドキュメントが導入されましたが、Kotlinでは初期バージョンから利用可能です。

コード例: ヒアドキュメント

Java 13以降:

public class Example {
    public static void main(String[] args) {
        String textBlock = """
                This is a text block.
                It allows multiple lines.
                No need for newline characters.
                """;
        System.out.println(textBlock);
    }
}

Kotlin:

val textBlock = """
    This is a text block.
    It allows multiple lines.
    No need for newline characters.
"""
println(textBlock)

2. Record型

Javaでは、シンプルなデータキャリアクラスを簡潔に定義するためにRecordがJava 16で導入されました。一方、Kotlinは当初からdata classを提供しており、より短い構文でデータキャリアクラスを定義できます。

コード例: Record型

Java 16以降:

public record User(String name, int age) {}

Kotlin:

data class User(val name: String, val age: Int)

3. Pattern Matching

JavaではJava 17からパターンマッチングが段階的に導入されていますが、Kotlinでは初期からwhen構文で簡単にパターンマッチングが可能です。

コード例: Pattern Matching

Java 17以降:

Object obj = "Hello";
switch (obj) {
    case String s -> System.out.println("String: " + s);
    case Integer i -> System.out.println("Integer: " + i);
    default -> System.out.println("Unknown type");
}

Kotlin:

val obj: Any = "Hello"
when (obj) {
    is String -> println("String: $obj")
    is Int -> println("Integer: $obj")
    else -> println("Unknown type")
}

4. デフォルト引数

Javaではデフォルト引数がサポートされていないため、代わりにメソッドオーバーロードで対応する必要があります。Kotlinは初期からデフォルト引数をサポートしており、メソッド定義が簡潔になります。

コード例: デフォルト引数

Java:

public class User {
    private String name;
    private int age;

    public User(String name) {
        this(name, 0);
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Kotlin:

data class User(val name: String, val age: Int = 0)

5. シングルトン

Kotlinはobjectキーワードを使ってシングルトンオブジェクトを簡単に定義できますが、Javaにはシングルトンの標準サポートがないため、通常はenumを使用するか、明示的な実装が必要です。

コード例: シングルトン

Java:

public enum Singleton {
    INSTANCE;
}

Kotlin:

object Singleton

5. チェック例外

Javaのチェック例外は、例外処理のために多くのtry-catchブロックが必要で、コードが冗長になります。Kotlinにはチェック例外がなく、開発者の判断でエラーハンドリングが行えます。

コード例

Java:

public void readFile(String path) {
    try {
        BufferedReader reader = new BufferedReader(new FileReader(path));
        // ファイル処理
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Kotlin:

fun readFile(path: String) {
    val reader = BufferedReader(FileReader(path))
    // ファイル処理
}

6. 記述量の多いアクセス修飾子の使用

Javaでは、クラスやメソッドにpublicprivateなどのアクセス修飾子を明示的に記述する必要があり、特にプロジェクトが大きくなるほど冗長に感じます。Kotlinはデフォルトでpublic扱いにするなど、アクセス制御がシンプルです。

コード例

Java:

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    private void calculateAge() { /* 年齢計算 */ }
}

Kotlin:

class User(val name: String, val age: Int) {
    private fun calculateAge() { /* 年齢計算 */ }
}

7. Nullチェックの記述が多い

Javaではnull安全が標準でサポートされていないため、nullチェックが必須となり、コードの可読性を低下させます。Kotlinは、Nullable型とNon-Nullable型の区別が明確なため、NullPointerExceptionを防ぎやすい設計になっています。

Kotlinは、Nullable型とNon-Nullable型が明確に区別されているため、NullPointerException(NPE)を防ぎやすい設計になっています。そもそもNon-Nullable型として宣言された変数には、nullを設定できないため、コンパイルエラーが発生します。これにより、開発段階でnullの割り当てミスを防ぐことができます。

Non-Nullable型の例
Kotlinでは、変数をString型として宣言すると、その変数にはnullを代入できません。これにより、意図しないNullPointerExceptionが防げます。

val name: String = "John"
// name = null  // エラー: Non-Nullable型の変数にnullを代入できません

上記の例でnameはNon-Nullable型として宣言されているため、nullの代入を試みるとコンパイルエラーが発生します。このように、Kotlinではnullableかどうかを明確に区別できるため、実行時にNPEが発生しにくくなっています。

Nullable型の例
Nullable型の変数を使用する場合は、?を付けて明示的にNullableとして宣言する必要があります。これにより、変数がnullの可能性があることが明確になります。

val name: String? = "John"
val emptyName: String? = null  // Nullable型にはnullを代入可能

このように、String?のように?を付けてNullable型として宣言することで、nullが代入できる変数となります。このNullable型とNon-Nullable型の区別により、KotlinはNPEを防ぎやすい設計となっています。

Kotlinのこうした設計により、変数宣言時点でNullableかNon-Nullableかを明確にでき、予期しないnullの割り当てを防ぐことが可能です。これによって、コードの安全性が高まり、実行時エラーも減少します。


比較表

特徴 Javaの特徴 Kotlinの特徴
冗長なコード Getter/Setterやequals等のコードが多い data classでシンプルな定義
Null安全の欠如 NullPointerExceptionが多発 Nullable型とNon-Nullable型を区別
ラムダ式と関数型の制限 ラムダ式が限定的、コレクション操作が冗長 豊富な関数型サポートで、シンプルなコレクション操作
古いバージョンの互換性問題 最新機能の普及に時間がかかる モダンな機能を取り入れやすい
チェック例外 try-catchで冗長なコードになる チェック例外がないため、シンプルなエラーハンドリング
記述量の多いアクセス修飾子 メソッドやフィールドにpublicprivateが必須 デフォルトpublicで簡潔なアクセス制御
Nullチェックの煩わしさ 開発者が手動でnullチェックを行う必要がある 言語レベルでのnull安全サポート
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?