Was ist besser, Kotlin oder zukünftiges Java?

Einführung

Dieser Artikel ist der 12. Tagesartikel von Kotlin Adventskalender 2019.

Ich denke, Kotlin soll im Vergleich zu Java modern sein. Der Release-Zyklus von Java findet jedoch alle sechs Monate statt und wird zu einer besseren Sprache als zuvor. Ist Java also nicht besser, wenn man die zukünftige Wartbarkeit berücksichtigt? Es gibt auch eine Meinung, dass Sie sich fragen, welche Sie bei der Auswahl einer Sprache wählen sollen. Daher möchte ich die neuesten Java-Trends mit Kotlin vergleichen und vergleichen, welche moderner sind.

Die Vergleichsziele sind die neueste Version der Funktionen von Kotlin und Java 12 oder höher. In Bezug auf Java wurde Version 13 am 18. September 2019 veröffentlicht, aber dieses Mal möchte ich einige Funktionen vergleichen, die wahrscheinlich in JDK 14 und später in der Entwicklung sind. Für Funktionen vor JDK11 gibt es meiner Meinung nach viele andere gute Artikel. Bitte beziehen Sie sich darauf. Oder es kann hinzugefügt werden, wenn ein Rand vorhanden ist. </ font>

In Bezug auf die Funktionen, die in jeder Version von JDK hinzugefügt wurden, ist der Artikel von @ nowokay ziemlich ordentlich und leicht zu verstehen, daher möchte ich darauf verweisen und ihn mit Kotlin vergleichen.

Übrigens hat Kotlin gerade erst angefangen, sich zu berühren, und ich denke, es gibt einige Teile, die ich nicht gut verstehe, also bitte Masakari.

Annahme

  • Kotlin 1.3.61
  • Java OpenJDK 14 Early-Access Build 25
    (Ich habe diesmal sdkman benutzt)

Vergleiche mit Java 12

Referenz: Zusammenfassung der neuen Java 12-Funktionen

Die Vorschau-Version von Switch Expressions ist ebenfalls in 12 enthalten. Da sie jedoch noch in JDK14 behoben wird, werde ich sie in JDK14 erläutern.

CompactNumberFormat CompactNumberFormat wurde in Java 12 eingeführt, Kotlin verfügt jedoch nicht über eine ähnliche Funktion.

Java


     NumberFormat cnf = NumberFormat.getCompactNumberInstance();
     System.out.println(cnf.format(10000));               //10 000
     System.out.println(cnf.format(10000_0000));          //Ein hundert Millionen
     System.out.println(cnf.format(10000_0000_0000L));    //1 Billion
}

Kotlin


    fun format(number: Long): String {
        return when {
            (number >= 10F.pow(12)) -> {
                floor(number / 10F.pow(12)).toLong().toString() + "Billion"
            }
            (number >= 10F.pow(8)) -> {
                floor(number / 10F.pow(8)).toLong().toString() + "Milliarde"
            }
            (number >= 10F.pow(4)) -> {
                floor(number / 10F.pow(4)).toLong().toString() + "Zehntausend"
            }
            else -> {
                number.toString()
            }
        }
    }
    println(format(10000))               //10 000
    println(format(10000_0000))          //Ein hundert Millionen
    println(format(10000_0000_0000L))    //1 Billion

Da es nur 3 Einheiten gibt, habe ich versucht, es ein wenig gewaltsam zu machen. Java ist hier besser, nicht wahr? Es scheint, dass es nur wenige Möglichkeiten gibt, es zu nutzen.

String.indent

Wenn Sie String.indent verwenden, wird es mit dem angegebenen eingerückten Argument angezeigt.

Java


    String s = "Kotlin";
    System.out.println(s);
    System.out.println(s.indent(2));
    // Kotlin
    //  Kotlin

Verwenden Sie im Fall von Kotlin String.prependIndent und rücken Sie mit dem darin angegebenen Zeichen ein. Dieses Mal habe ich 2 leere Zeichen eingefügt, sodass 2 Zeichen eingerückt werden.

Kotlin


    val s = "Kotlin"
    println(s)
    println(s.prependIndent("  "))
    // Kotlin
    //  Kotlin

String.transform

Java


    var addresses = Map.of("Mike", "Fukuoka", "John", "Tokyo");
    var population = Map.of("Tokyo", 30000000, "Fukuoka", 2000000);
    var name = "Mike";
    System.out.println(name.transform(addresses::get).transform(population::get));  // 2000000

Kotlin


fun main() {
    val addresses = mapOf("Mike" to "Fukuoka", "John" to "Tokyo")
    val population = mapOf("Tokyo" to 30000000, "Fukuoka" to 2000000)
    val name = "Mike"

    println(name.let(addresses::get)?.let(population::get))  // 2000000
}

In Kotlin können Sie es auf die gleiche Weise wie String.transform schreiben, indem Sie die Funktion let verwenden.

@ rmakiyama und @ anatawa12 haben mir beigebracht, wie man String.transform in Kotlin schreibt! Vielen Dank. </ font>

Collectors.teeing

Dies scheint auch mit einer Linie mit Kotlin schwer zu erreichen zu sein. Ich habe stattdessen eine Funktion vorbereitet und realisiert.

Java


    Map.Entry<String, Long> map = Stream.of("aaa", "", "bbb", "ccc").
                filter(Predicate.not(String::isEmpty)).
                collect(Collectors.teeing(
                    Collectors.joining(","),
                    Collectors.counting(),
                    Map::entry));
    System.out.println(map);  // aaa,bbb,ccc=3

Kotlin


    fun listToMap(list: List<String>): Map<String, Int> {
        return mutableMapOf(list.joinToString(",") to list.count())
    }
    val list = mutableListOf("aaa", "", "bbb", "ccc")
                    .filter { !it.isBlank() }
                    .toList()
    println(listToMap(list))  // {aaa,bbb,ccc=3}

Files.mismatch

Es ist eine API, um zu überprüfen, ob der Inhalt der Datei unterschiedlich ist.

Java


	Path filePath1 = Path.of("./com/example/jdk12/FilesMismatchFile1.txt");
	Path filePath2 = Path.of("./com/example/jdk12/FilesMismatchFile2.txt");

	long mismatchRow = Files.mismatch(filePath1, filePath2);
	if (mismatchRow == -1) {
	    System.out.println("file is same");
	} else {
	    System.out.println("file is diffarent¥nrow:" + String.valueOf(mismatchRow));
	}
        //Wenn die Dateien gleich sind
        // file is same

        //Wenn die Dateien unterschiedlich sind
        // file is diffarent
        // row:24

Kotlin scheint keine ähnliche API zu haben. Da es schwierig ist, den Prozess zu schreiben, werde ich ihn hier weglassen.

String.isEmpty

Java


    String s = "";
    String n = null;
    if (s.isEmpty()) {
        System.out.println("s is Empty");
    }
    if (n.isEmpty()) {
        System.out.println("n is Empty");
    }

    // s is Empty
    // Exception in thread "main" java.lang.NullPointerException
    //   at com.example.jdk12.StringIsEmpty.main(StringIsEmpty.java:10)

Kotlin


    val s = "";
    val n: String? = null;
    if (s.isEmpty()) {
        println("s is Empty")
    }
    if (n.isNullOrEmpty()) {
        println("n is Null")
    }

    // s is Empty
    // n is Null

Kotlin erhält einen Kompilierungsfehler, wenn n.isEmpty () verwendet wird. Nullsicherheit ist schließlich gut.

Wenn Sie in Java "isEmpty ()" für eine Null-Variable verwenden, gibt NPE diese aus. Sie können auch mit Optional <String> n = null; kompilieren, und NPE wird auftreten. Ich frage mich, ob Java in der Lage sein wird, gleichzeitig Null- und Leerprüfungen durchzuführen, ohne eine Bibliothek zu verwenden.

Vergleiche mit Java 13

Referenz: https://openjdk.java.net/projects/jdk/13/

Switch-Ausdrücke ändern sich wahrscheinlich etwas mehr von JDK 13, daher werde ich sie später vergleichen.

Text Blocks

https://openjdk.java.net/jeps/368 Es wird sich wahrscheinlich ein bisschen mehr ändern, aber ich denke nicht, dass es sich viel ändern wird.

Java


    String s = """
	       ultra soul
	       OCEAN
	       """;
    System.out.println(s);
    // ultra soul
    // OCEAN

Kotlin


    val s = """
            ultra soul
            OCEAN
            """
    print(s)
    // ultra soul
    // OCEAN

Für Java und Kotlin ist es fast dasselbe.

Vergleiche mit Java 14 oder höher

Nun, es wurde nicht offiziell von hier veröffentlicht, aber vergleichen wir Kotlin mit den Inhalten, die sich in der Entwicklung befinden.

Referenz: Änderungen der Java-Syntax werden von Amber berücksichtigt

Records(JEP 359)

Datensätze bereiten automatisch Code wie hashCode`` gleich`` toString vor, der in Java-Beans redundant ist. Es ist sehr einfach und schön.

Java


    record Point(int x, int y) {}

    Point point1 = new Point(5, 10);
    Point point2 = new Point(5, 10);
    System.out.println(point1.x());                   // 5
    System.out.println(point1);                       // Point[x=5, y=10]
    System.out.println(point1.equals(point2));        // true

Und Kotlin hat eine Datenklasse.

Kotlin


    data class Point(val x: Int, val y: Int)
    val point1 = Point(5, 10)
    val point2 = Point(5, 10)
    println(point1.x)                 // 5
    println(point1)                   // Point(x=5, y=10)
    println(point1.equals(point2))    // true

Der Datensatz scheint sich auf die Fallklasse von Scala, die Datenklasse von Kotlin, die Datensatztypen von C # usw. zu beziehen.

Sealed Types(JEP 360)

Versiegelte Typen werden verwendet, um die Klassenvererbung zu begrenzen Dies ist bequem als Aufzählung zu verwenden.

Java


    sealed interface HondaCar {};

    public class Demio implements HondaCar {
        public String getName() {
            return "Demio";
        }
    }

    public class Vezel implements HondaCar {
        public String getName() {
            return "Vezel";
        }
    }

Kotlin


sealed class HondaCar
class Demio: HondaCar() {
    fun getName():String { return "Demio" }
}
class Vezel: HondaCar() {
    fun getName():String { return "Vezel" }
}

Aufnahme einschalten

Ich verstehe den Java-Code hier nicht sehr gut, aber ich werde ein Bild schreiben. (Ich frage mich, ob ich irgendwo ein Beispiel für die Verwendung von Sealed / Record mit Switch gesehen habe, aber ich habe noch keine Probleme gefunden. Wenn ich eines finde, füge ich die detaillierte URL ein.)

Java


    // sealed
    sealed interface HondaCar permits Demio, Vezel {}
    record Demio() implements HondaCar {}
    record Vezel() implements HondaCar {}

    // use
    int price = switch(hondaCar) {
                    case Demio(int price) -> "Demio";
                    case Vezel(int price) -> "Vezel";
                    //Keine Notwendigkeit für eine Standardanweisung, da versiegelt weiß, dass die einzigen Optionen Demio und Vezel sind
                    // default -> throw new IllegalStateException("Error");
                };

Kotlin ist bereits im when-Ausdruck verfügbar.

Kotlin


    // sealed
    sealed class HondaCar
    class Demio: HondaCar()
    class Vezel: HondaCar()

    // use
   val hondaName = when(hondaCar) {
        is Demio -> "Demio"
        is Vezel -> "Vezel"
        //Kein Standard erforderlich
    }
    println(hondaName)

Übrigens, wenn Sie nicht mit Kotlin versiegelt verwenden, ist Standard (sonst) erforderlich.

Kotlin-Fehler


    // interface
    interface NissanCar
    class Leaf: NissanCar
    class Juke: NissanCar

    // use
    val nissanCar: NissanCar = Leaf()
    val nissanName = when(nissanCar) {
        is Leaf -> "Leaf"
        is Juke -> "Juke"
        //Da es sonst nichts gibt, wird der folgende Fehler ausgegeben
        // 'when' expression must be exhaustingstive, add necssary 'else' branch
    }
    println(nissanName)

Switch Expressions (JEP 361)

https://openjdk.java.net/jeps/361 Ursprünglich dachte ich, dass der Java-Switch schwierig zu verwenden ist, aber er wurde seit JDK12 (JEP 325) in Betracht gezogen, und es scheint, dass verschiedene Verbesserungen vorgenommen wurden. Ich möchte es nur mit Kotlin vergleichen, deshalb werde ich nur die Teile beschreiben, die sich signifikant ändern.

Es können mehrere Fälle beschrieben werden, die in denselben Block passen

Java


    //ursprünglich
    switch (day) {
        case MONDAY:
        case FRIDAY:
        case SUNDAY:
            System.out.println(6);
            break;
        case TUESDAY:
            System.out.println(7);
            break;
    }

    //Nach der Verbesserung
    switch (day) {
        case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
        case TUESDAY                -> System.out.println(7);
        case THURSDAY, SATURDAY     -> System.out.println(8);
        case WEDNESDAY              -> System.out.println(9);
    }

Es können mehrere Kotlins gleichzeitig aufgelistet werden.

Kotlin


    when (day) {
        Day.MONDAY, Day.FRIDAY, Day.SUNDAY -> println(6)
        Day.TUESDAY -> println(7)
        Day.THURSDAY,  Day.SATURDAY -> println(8)
        Day.WEDNESDAY -> println(9)
    }

Schalter kann als Ausdruck verwendet werden

Wie unter Versiegelte Typen erwähnt, scheint das Ergebnis des Wechsels als Ausdruck verwendet und in einer Variablen gespeichert zu werden. Derzeit scheint bei Verwendung von switch in einer Schleife wie einer for-Anweisung die Bewegung von break and continue angepasst zu werden.

Java


    int j = switch (day) {
        case MONDAY  -> 0;
        case TUESDAY -> 1;
        default      -> {
            int k = day.toString().length();
            int result = f(k);
            yield result;
        }
    };

Kotlin


    val j = when (day) {
        is Monday -> 0;
        is Tuesday -> 1;
        else -> {
            val k = day.toString().length
            k
        }
    }

    //Kotlin unterstützt auch break and continue im Switch(Es ist nutzlose Logik)
    loop@ for (i in 1..100) {
        when (day) {
            is Monday -> j = 0;
            is Tuesday -> j = 1;
            else -> {
                j = day.toString().length
                break@loop
            }
        }
    }

Pattern Matching for instanceof (JEP 305)

Nach dem Überprüfen des Typs mit instanceof ist der Typ festgelegt, sodass "Pattern Matching for instanceof" verwendet werden kann, indem er in einer Variablen gespeichert wird, wie er ist.

Java


    //bis jetzt
    if (o instanceof String) {
        //Sie können o nicht direkt als String-Typ verwenden
        // System.out.println(o.length());

        //Es ist notwendig, es einmal in den String-Typ umzuwandeln und dann zu verwenden
        String s = (String)o;
        System.out.println(s.length());     // 27
    }

    //von jetzt an
    Object o = "Pattern Match of instanceof";
    //Kann gleichzeitig mit instanceof in einer Variablen gespeichert werden
    if (o instanceof String s) {
        System.out.println(s.length());    // 27
    }

    //Es scheint, dass der Schalter auch verfügbar sein wird.(OpenJDK 14 Early-Noch in Access Build 25)
    // https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html
    switch (o) {
        case Integer i -> System.out.println(i);
        case String s -> System.out.println(s.length());
    }

Kotlin kann unverändert verwendet werden, ohne es in einer Variablen zu speichern.

Kotlin


    val o: Any = "Pattern Match of instanceof"
    if (o is String) {
        println(o.length)    // 27
    }
    when(o) {
        is Int -> println("Int")
        is String -> {
            val s:String = o
            println(s)
        }
    }
    // 27

Zusammenfassung

Zunächst fand ich es erstaunlich, dass sich Java so stark weiterentwickelt hat.

Zuvor war Kotlin der Beste. Ich denke, Java war ein enttäuschendes Bild, aber ich habe den Eindruck, dass Java auch moderne Sprachen wie Kotlin in Richtung JDK17 übernommen hat und der Unterschied verschwindet. Und ich war überrascht von Kotlin, das bereits eine so neue Java-Syntax übernommen hat.

Unter dem Gesichtspunkt des Sprachvergleichs zwischen Java und Kotlin bietet Kotlin immer noch Vorteile wie NULL-Sicherheit und einfaches Schreiben von Funktionstypen, und ab September 2021, wenn das nächste LTS, JDK17, veröffentlicht wird, welches Ich fragte mich, ob ich es wählen könnte.

Ich werde in Kotlin für das neue Projekt schreiben, das ich machen werde, daher möchte ich einen separaten Artikel darüber schreiben, woran ich interessiert war, während ich mich in Zukunft tatsächlich damit beschäftige.

Schließlich

Es ist ein Spiegelbild, dass die Farbe von Java stärker geworden ist, obwohl es eine Werbung von Kotlin ist.

Es tut mir auch leid, dass der Ausdruck so geworden ist, dass andere Personen den bereits im Artikel enthaltenen Inhalt wiederverwenden. Für den Vergleich zwischen Kotlin und dem neuen Java gab es viele Inhalte, die ich unweigerlich unter dem Mangel an Informationen leiden würde. Bitte verzeihen Sie mir.

Ich dachte beim Schreiben, aber es scheint besser, JDK9 auch mit JDK11 zu vergleichen. Wir werden antworten, wenn Sie es sich leisten können.

Kotlin hat es im Moment im Geschäft nicht angerührt, daher denke ich, dass es einige Teile gibt, die ich nicht verstehe. Deshalb möchte ich es schrittweise korrigieren, wenn sich mein Verständnis vertieft.

Referenzinformationen

Zusammenfassung der neuen Funktionen von Java 12 Änderungen der Java-Syntax werden von Amber berücksichtigt

https://openjdk.java.net/projects/jdk/13/ https://openjdk.java.net/projects/amber/

Recommended Posts