This article is the 12th day article of Kotlin Advent Calendar 2019.
I think Kotlin has been said to be modern compared to Java. However, the release cycle of Java is once every six months, and it is becoming a better language than before, so Java is better when considering future maintainability. There is also an opinion that you may be wondering which one to choose when selecting a language. So, this time, I would like to compare the latest Java trends with Kotlin and compare which one is more modern.
The comparison targets are the latest version of Kotlin and Java 12 or later features. Regarding Java, version 13 was released on September 18, 2019, but this time I would like to compare some features that are likely to enter JDK 14 and later under development. For the features before JDK11, I think there are many other good articles, so please refer to them. Or it may be added when there is a margin. </ font>
Regarding the functions added in each version of the JDK, @ nowokay's article is pretty neat and easy to understand, so I would like to refer to it and compare it with Kotlin.
By the way, Kotlin has just begun to touch and I think there are some parts that I do not understand well, so please do Masakari.
Reference: Summary of new Java12 features
The Preview version of Switch Expressions is also included in 12, but since it is still being fixed in JDK 14, I will explain it in JDK 14.
CompactNumberFormat CompactNumberFormat was introduced in Java 12, but Kotlin doesn't have a similar feature.
Java
NumberFormat cnf = NumberFormat.getCompactNumberInstance();
System.out.println(cnf.format(10000)); //10 000
System.out.println(cnf.format(10000_0000)); //One hundred million
System.out.println(cnf.format(10000_0000_0000L)); //1 trillion
}
Kotlin
fun format(number: Long): String {
return when {
(number >= 10F.pow(12)) -> {
floor(number / 10F.pow(12)).toLong().toString() + "Trillion"
}
(number >= 10F.pow(8)) -> {
floor(number / 10F.pow(8)).toLong().toString() + "Billion"
}
(number >= 10F.pow(4)) -> {
floor(number / 10F.pow(4)).toLong().toString() + "Ten thousand"
}
else -> {
number.toString()
}
}
}
println(format(10000)) //10 000
println(format(10000_0000)) //One hundred million
println(format(10000_0000_0000L)) //1 trillion
Since there are only 3 units, I tried to make it a little forcibly. Java is better here, isn't it? It seems that there are few opportunities to use it.
String.indent
If String.indent is used, the specified argument will be indented and displayed.
Java
String s = "Kotlin";
System.out.println(s);
System.out.println(s.indent(2));
// Kotlin
// Kotlin
In the case of Kotlin, use String.prependIndent and indent with the character specified in it. This time, I put 2 blank characters, so 2 characters will be indented.
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, you can write it in the same way as String.transform by using the let function.
@rmakiyama and @ anatawa12 taught me how to write a String.transform in Kotlin! Thank you very much. </ font>
Collectors.teeing
This also seems difficult to achieve in one line with Kotlin. I prepared a function instead and realized it.
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
It is an API to check if the contents of File are different.
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));
}
//If the files are the same
// file is same
//If the files are different
// file is diffarent
// row:24
Kotlin doesn't seem to have a similar API. Since it is difficult to write the process, I will omit it here.
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 gets a compile error when using n.isEmpty ()
.
After all Null safety is good.
In Java, if you use ʻisEmpty () for a Null variable, NPE will output it. You can also compile using ʻOptional <String> n = null;
and NPE will occur.
I wonder if Java will be able to do Null and Empty checks at the same time without using a library.
Reference: https://openjdk.java.net/projects/jdk/13/
Switch Expressions are likely to change a little more from JDK 13, so I'll compare them later.
Text Blocks
https://openjdk.java.net/jeps/368 It's likely to change a little more, but I don't think it will change much.
Java
String s = """
ultra soul
OCEAN
""";
System.out.println(s);
// ultra soul
// OCEAN
Kotlin
val s = """
ultra soul
OCEAN
"""
print(s)
// ultra soul
// OCEAN
It's almost the same in Java and Kotlin.
Well, it's not officially released from here yet, but let's compare Kotlin with what is under development.
Reference: Java syntax changes being considered by Amber
Records(JEP 359)
Records automatically prepares code such as hashCode
ʻequals`` toString`, which is redundant in Java beans.
It's very simple and nice.
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
And Kotlin has a data class.
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
Record seems to refer to Scala's case class, Kotlin's data class, C #'s record types, etc.
Sealed Types(JEP 360)
Sealed Types are used to limit class inheritance This is convenient to use as an enum.
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" }
}
I don't understand Java code very much here, but I will write an image. (I'm wondering if I saw an example of using sealed / record with switch somewhere, but I haven't found any issues yet, so if I find one, I'll paste the detailed URL)
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";
//No need for default statement as sealed knows that Demio and Vezel are the only choices
// default -> throw new IllegalStateException("Error");
};
Kotlin is already available in when expressions.
Kotlin
// sealed
sealed class HondaCar
class Demio: HondaCar()
class Vezel: HondaCar()
// use
val hondaName = when(hondaCar) {
is Demio -> "Demio"
is Vezel -> "Vezel"
//No default required
}
println(hondaName)
By the way, if you don't use sealed in Kotlin, default (else) is required.
Kotlin error
// interface
interface NissanCar
class Leaf: NissanCar
class Juke: NissanCar
// use
val nissanCar: NissanCar = Leaf()
val nissanName = when(nissanCar) {
is Leaf -> "Leaf"
is Juke -> "Juke"
//Since there is no else, the following error is output
// 'when' expression must be exhaustingstive, add necssary 'else' branch
}
println(nissanName)
Switch Expressions (JEP 361)
https://openjdk.java.net/jeps/361 Originally I thought that Java switch was difficult to use, but it has been considered since JDK12 (JEP 325), and it seems that various improvements have been made. I just want to compare it with Kotlin, so I'll just list the parts that change a lot.
Java
//originally
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
}
//After improvement
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);
}
Multiple Kotlin can be listed at the same time.
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)
}
As mentioned in Sealed Types, it seems that the result of switch can be used as an expression and stored in a variable. Currently, when using switch in a loop such as a for statement, it seems that the movement of break and continue is being adjusted.
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 also supports break and continue in the switch(It's useless logic)
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)
After checking the type with instanceof, the type is fixed, so Pattern Matching for instanceof
can be used by storing it in a variable as it is.
Java
//until now
if (o instanceof String) {
//You cannot use o directly as a String type
// System.out.println(o.length());
//It is necessary to cast it to String type once and then use it.
String s = (String)o;
System.out.println(s.length()); // 27
}
//from now on
Object o = "Pattern Match of instanceof";
//Can be stored in a variable at the same time as instanceof
if (o instanceof String s) {
System.out.println(s.length()); // 27
}
//It seems that switch will also be available.(OpenJDK 14 Early-Still 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 can be used as it is without storing it in a variable.
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
First of all, I thought it was amazing that Java has evolved so much.
Previously Kotlin was the best. I think Java was a disappointing image, but I have the impression that Java has also adopted modern languages such as Kotlin toward JDK17, and the difference is disappearing. And I was surprised at Kotlin, which has already adopted such a new Java syntax.
From the perspective of comparing Java and Kotlin languages, Kotlin still has the advantages of being null-safe and easy to write functional types, and as of September 2021, when the next LTS, JDK17, is released, which one I wondered if I could choose it.
I will write in Kotlin for the new project I will make from now on, so I would like to write a separate article about what I was interested in while actually dealing with it in the future.
It is a reflection that the color of Java has become stronger even though it is an ad-care of Kotlin.
Also, I'm sorry that it has become an expression that other people reuse the content already in the article. For the comparison between Kotlin and the new Java, there was a lot of content that I inevitably suffered even though there was little information, so please forgive me.
I thought while writing, but it seems better to compare JDK9 to JDK11 as well. We will respond when you can afford it.
Kotlin hasn't touched it in business at the moment, so I think there are some parts that I don't understand, so I'd like to gradually correct it as my understanding deepens.
Java 12 new feature summary Java syntax changes being considered by Amber
https://openjdk.java.net/projects/jdk/13/ https://openjdk.java.net/projects/amber/
Recommended Posts