写点什么

从头学 Java17- 今天的 Kotlin 更香吗

作者:烧霞
  • 2023-07-15
    广西
  • 本文字数:10606 字

    阅读完需:约 35 分钟

出于各种限制,很多公司依然停留在 Java8,部分小伙伴转向了 Kotlin。Kotlin 作为静态编译语言,提供大量语法糖,而且编译后的字节码跟 Java 一致。

当时,Java8 于 2014 年发布,Kotlin 于 2016 年,很多宣称的语法糖都是对比的 Java8。不禁要问,相对今天的 Java17,Kotlin 优势还在吗?

现在就用最新的 Kotlin1.9.0,对前三篇文章里的 lambda、StreamAPI 依次改造,实践出真知!


编写 lambda、调用

Java

import Java.util.*;import Java.util.function.*;
/** * * @author 烧哥burn.red */public class Test1 { public static void main(String[] args) { Predicate<String> predicate = s -> s.length() == 3; Consumer<String> consumer = s -> System.out.println(s); Supplier<String> supplier = () -> "Hello Duke!"; Function<String, Integer> function = s -> s.length(); IntSupplier intSupplier = () -> 1; IntConsumer intConsumer = s -> System.out.println(s); IntPredicate intPredicate = i -> i > 10; ToIntFunction<String> toIntFunction = s -> s.length(); UnaryOperator<String> unaryOperator = s -> s.toUpperCase(); BiConsumer<String, Integer> biConsumer = (s, number) -> s.indexOf(number); ObjIntConsumer<String> objIntConsumer = (s, value) -> System.out.printf("%s,%d\n", s, value); BiPredicate<String, Integer> biPredicate = (word, length) -> word.length() == length; BiFunction<String, String, Integer> biFunction = (word, sentence) -> sentence.indexOf(word); ToIntBiFunction<String, String> toIntBiFunction = (word, sentence) -> sentence.indexOf(word); String a = "aaa"; if (predicate.test(a)) { consumer.accept(a); supplier.get(); function.apply(a); intConsumer.accept(1); intSupplier.getAsInt(); intPredicate.test(11); toIntFunction.applyAsInt(a); unaryOperator.apply(a); biConsumer.accept(a, 2); objIntConsumer.accept(null, 1); biPredicate.test(a, 3); biFunction.apply("fdsa", a); toIntBiFunction.applyAsInt("fdsa", a); } List<String> strings = new ArrayList<>(List.of("a", "bb", "ccc")); strings.forEach(consumer); strings.removeIf(predicate);//不应该在不可变集合上调用 System.out.println(strings); strings = Arrays.asList("a", "bb", "ccc"); strings.replaceAll(unaryOperator); System.out.println(strings);
// int i = 0;// Consumer<Integer> add = s -> i++;//报错,从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量 }}
复制代码

Kotlin

/** * * @author 烧哥burn.red */
fun main() { val predicate = { s: String -> s.length == 3 } val consumer = { s: String? -> println(s) } val supplier = { "Hello Duke!" } val function = { s: String -> s.length }
val intSupplier = { 1 } val intConsumer = { s: Int -> println(s) } val intPredicate = { i: Int -> i > 10 } val toIntFunction = { s: String -> s.length } val unaryOperator = { s: String -> s.uppercase() }
val biConsumer = { s: String, number: Int -> s.indexOf(number.toChar()) } val objIntConsumer = { s: String?, value: Int -> println("$s,$value") } val biPredicate = { word: String, length: Int -> word.length == length } val biFunction = { word: String?, sentence: String -> sentence.indexOf(word!!) } val toIntBiFunction = { word: String?, sentence: String -> sentence.indexOf(word!!) }
val a = "aaa"
if (predicate(a)) { consumer(a) supplier() function(a)
intConsumer(1) intSupplier() intPredicate(11) toIntFunction(a) unaryOperator(a)
biConsumer(a, 2) objIntConsumer(null, 1) biPredicate(a, 3) biFunction("fdsa", a) toIntBiFunction("fdsa", a) } var strings = mutableListOf("a", "bb", "ccc") strings.forEach(consumer) strings.removeIf(predicate) //不应该在不可变集合上调用 println(strings)
strings = arrayListOf("a", "bb", "ccc") strings.replaceAll(unaryOperator) println(strings)
var i = 0 val add = { s: Int? -> i++ } //不报错 add(i) println(i)}
复制代码


可以看出:


  • Kotlin 的 lambda,没有那四种划分,调用时类似函数,(参数..),非常简洁

  • Kotlin 的 lambda,可以改变外层变量的值

  • Kotlin 没有自己的 removeIf,replaceAll,但可以直接调用 Java 的

  • Java 为原始类型准备了特别版,Kotlin 默认都是原始类型

  • Kotlin 变量默认都是非 null


这一局,Kotlin 胜出。

方法引用、链接

Java

import red.burn.bean.User;
import Java.util.*;import Java.util.function.*;import Java.util.logging.Logger;
/** * @author 烧哥burn.red */public class Test2 {
public static void main(String[] args) { //方法引用 DoubleUnaryOperator sqrt = Math::sqrt; IntBinaryOperator max = Integer::max;//静态方法引用 Supplier<List<String>> newListOfStrings = ArrayList::new;//构造方法引用 Consumer<String> printer = System.out::println;//绑定到System.out Function<String, Integer> toLength = String::length;//非绑定,绑定到String的实例
//Lambla的链接 Predicate<String> isNull = Objects::isNull; Predicate<String> isEmpty = String::isEmpty; Predicate<String> isNullOrEmpty = isNull.or(isEmpty); Predicate<String> isNotNullNorEmpty = isNullOrEmpty.negate(); Predicate<String> shorterThan5 = s -> s.length() < 5; Predicate<String> p = isNotNullNorEmpty.and(shorterThan5);
Logger logger = Logger.getLogger("MyApplicationLogger"); Consumer<String> log = logger::info; Consumer<String> printStr = System.out::println; Consumer<String> printAndLog = log.andThen(printStr);// printAndLog.accept("test");
Function<String, Integer> function1 = String::length; Function<Integer, Integer> function2 = s -> ++s; Function<String, Integer> function = function1.andThen(function2); System.out.println("new=" + function.apply("abc")); //4
Function<String, String> id = Function.identity();
//Comparator Comparator<Integer> comparator = Integer::compare; Comparator<String> comparator1 = (s1, s2) -> Integer.compare(s1.length(), s2.length()); Comparator<String> comparator2 = (s1, s2) -> Integer.compare(toLength.apply(s1), toLength.apply(s2)); Comparator<String> comparator3 = Comparator.comparing(String::length);
Comparator<User> byFirstName = Comparator.comparing(User::getFirstName); Comparator<User> byLastName = Comparator.comparing(User::getLastName); Comparator<User> byFirstNameThenLastName = byFirstName.thenComparing(byLastName) .thenComparingInt(User::getAge); Comparator<User> byFirstNameThenLastName1 = Comparator.comparingInt(User::getAge) .thenComparing( Comparator.nullsLast(Comparator.naturalOrder()));
List<String> strings = Arrays.asList("one", "two", "three", "four", "five"); strings.sort(comparator3.reversed()); System.out.println(strings); }}
复制代码


import lombok.Builder;import lombok.Data;import org.jetbrains.annotations.NotNull;
/** * @author 烧哥burn.red */@Data@Builderpublic class User implements Comparable<User> {
private String name; private int age; private String firstName; private String lastName;
@Override public int compareTo(User o) {
return this.name.compareTo(o.name); }}
复制代码

Kotlin

import Java.util.logging.Loggerimport Kotlin.math.sqrtimport red.burn.bean.UserKTimport Kotlin.math.max
/** * * @author 烧哥burn.red */fun main() { //方法引用 val sqrt = ::sqrt// val max = ::max //报错,歧义// val newListOfStrings = ::ArrayList //报错,歧义// val printer =::println //报错,歧义 val ar = 5.run<Int, ArrayList<String>>(::ArrayList) "a".run(::println) val kt = ::UserKT //构造方法引用 val user = kt("abc", 10) var (name, age) = UserKT("csc") val firstName = user::firstName //属性引用 val addAge = user::addAge //函数引用 val toLength = String::length //非绑定,绑定到String的实例
//Lambla的链接 val isNull = { obj: String? -> obj == null } val isEmpty = { obj: String -> obj.isEmpty() } val isNullOrEmpty = { obj: String? -> obj == null || isEmpty(obj) } val isNotNullNorEmpty = { obj: String? -> !isNullOrEmpty(obj) } val shorterThan5 = { s: String -> s.length < 5 } val p = { s: String -> isNotNullNorEmpty(s).and(shorterThan5(s)) }
val logger = Logger.getLogger("MyApplicationLogger") val log = { message: String? -> logger.info(message) } val printStr = { message: String? -> println(message) } val printAndLog = { message: String? -> log(message).also { printStr(message) } } printAndLog("test")
val function1 = String::length// val function2 = { s: Int -> ++s }//报错 Val cannot be reassigned val function2 = { s: Int -> var i = s; ++i; } val function = { s: String -> function1(s).let(function2) } println("new=" + function("abc")) //4
val id = { s: String? -> s }
//Comparator val comparator = { x: Int, y: Int -> (x).compareTo(y) } val comparator1 = { s1: String, s2: String -> s1.length.compareTo(s2.length) } val comparator2 = { s1: String, s2: String -> toLength(s1).compareTo(toLength(s2)) } val comparator3 = compareBy(String::length)
val byFirstName = compareBy(UserKT::firstName) val byLastName = compareBy(UserKT::lastName) val byFirstNameThenLastName = byFirstName.then(byLastName).thenBy(UserKT::age) val byFirstNameThenLastName1 = compareBy(UserKT::age).then(nullsLast(naturalOrder()))
val strings = arrayListOf("one", "two", "three", "four", "five") strings.sortWith(comparator3.reversed()) println(strings)}
复制代码


/** * * @author 烧哥burn.red */data class UserKT(var name: String, var age: Int = 1) : Comparable<UserKT> {
var firstName: String? = null var lastName: String? = null
override fun compareTo(other: UserKT): Int { return name.compareTo(other.name) }
fun printUser(s: UserKT) { println(s) }
fun addAge(i: Int, j: Int): Int { return i + j }
}
复制代码


可以看出:


  • Kotlin 的 lambda,可以有类引用、函数引用、属性引用、构造引用,其中函数引用不能有歧义

  • Kotlin 的 lambda,因为没有四种划分,缺乏 Java 里丰富的链接方式,不过可以自己实现

  • Kotlin 的 lambda,无法修改自己的参数,只能读取

  • Kotlin 可读性比较强,Java 容易看的分神


这一局,Kotlin 跟 Java 打平。

StreamAPI

Java

import Java.util.List;import Java.util.Map;import Java.util.function.*;import Java.util.stream.*;
/** * @author 烧哥burn.red */public class Test3 { public static void main(String[] args) { //flatmap Function<String, Stream<Integer>> flatParser = s -> { try { return Stream.of(Integer.parseInt(s)); } catch (NumberFormatException e) { } return Stream.empty(); }; List<String> strings = List.of("1", " ", "2", "3 ", "", "3"); List<Integer> ints = strings.stream().flatMap(flatParser).toList(); System.out.println("ints = " + ints); //mapMulti ints = strings.stream().<Integer>mapMulti((string, consumer) -> { try { consumer.accept(Integer.parseInt(string)); } catch (NumberFormatException ignored) { } }).toList(); System.out.println("ints = " + ints); List<Integer> ints2 = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9); List<Integer> result = ints2.stream().skip(2).limit(5).toList(); System.out.println("result = " + result); List<Integer> list0 = List.of(1, 2, 3); List<Integer> list1 = List.of(4, 5, 6); List<Integer> list2 = List.of(7, 8, 9);// 1st pattern: concat List<Integer> concat = Stream.concat(list0.stream(), list1.stream()).toList();// 2nd pattern: flatMap List<Integer> flatMap = Stream.of(list0.stream(), list1.stream(), list2.stream())//类似city的外层组成的流 .flatMap(Function.identity()).toList(); System.out.println("concat = " + concat); System.out.println("flatMap = " + flatMap); //reduce Stream<String> strings1 = Stream.of("one", "two", "three", "four"); BinaryOperator<Integer> combiner = Integer::sum; Function<String, Integer> mapper = String::length; BiFunction<Integer, String, Integer> accumulator = (partialReduction, element) -> partialReduction + mapper.apply(element); int result1 = strings1.reduce(0, accumulator, combiner); System.out.println("sum = " + result1); //groupby map List<String> strings2 = List.of("two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"); Map<Integer, Long> histogram = strings2.stream().collect(Collectors.groupingBy(String::length, Collectors.counting())); histogram.forEach((k, v) -> System.out.println(k + " :: " + v)); Map<Long, List<Integer>> map = histogram.entrySet() .stream() .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList()))); Map.Entry<Long, List<Integer>> result2 = map.entrySet().stream().max(Map.Entry.comparingByKey())//再求max .orElseThrow(); System.out.println("result = " + result2); }}
复制代码


Kotlin


/** * @author 烧哥burn.red */fun main() {        //flatmap    val flatParser = label@{ s: String ->        try {            return@label listOf(s.toInt())        } catch (_: NumberFormatException) {        }        emptyList<Int>()    }
val strings = listOf("1", " ", "2", "3 ", "", "3") var ints = strings.flatMap(flatParser) println("ints = $ints")
//mapMulti /*ints = strings.mapMulti { string: String, consumer: Consumer<Int?> -> try { consumer.accept(string.toInt()) } catch (ignored: NumberFormatException) { } } println("ints = $ints")*/
val ints2 = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9) val result = ints2.drop(2).take(5) println("result = $result")
val list0 = listOf(1, 2, 3) val list1 = listOf(4, 5, 6) val list2 = listOf(7, 8, 9)// 1st pattern: concat val concat = list0 + list1// 2nd pattern: flatMap val flatMap = listOf(list0, list1, list2).flatten() println("concat = $concat") println("flatMap = $flatMap")
//reduce val strings1 = listOf("one", "two", "three", "four") val mapper = String::length val accumulator = { partialReduction: Int, element: String -> partialReduction + mapper(element) } val result1 = strings1.fold(0, accumulator) println("sum = $result1")
//groupby map val strings2 = listOf("two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve") val histogram: Map<Int, Int> = strings2.groupingBy { it.length }.eachCount() histogram.forEach({ k, v -> println("$k :: $v") })
val map = histogram.map { it }.groupBy({ it.value }, { it.key }).maxBy { it.key } println(map)
}
复制代码


可以看出:


  • Kotlin 的 lambda,不支持 multiMap,但可以自己实现

  • Kotlin 有运算符重载,可以对集合进行+-

  • flatten简化了flatmap,fold简化了reduce,eachCount简化了分组计数

  • 集合直接就是流,集合上的groupby等直接调用,不需要collect()

  • 函数很多有混淆,像groupBygroupingBymaxBymaxOf

  • 不过中间可能有 null,还需要人工判断,不如 Java,Optional 总不会报错

  • groupBy({ it.value }, { it.key }).maxBy { it.key }这个能亮瞎


总体来说,代码量减少非常多,这局 Kotlin 胜出。

综合

Java

import red.burn.bean.*;
import Java.util.*;import Java.util.function.BiFunction;import Java.util.function.Function;import Java.util.stream.*;
/** * * @author 烧哥burn.red */public class Test4 { public static void main(String[] args) { Author au1 = new Author("Au1"); Author au2 = new Author("Au2"); Author au3 = new Author("Au3"); Author au4 = new Author("Au4"); Author au5 = new Author("Au5"); Article a1 = new Article("a1", 1991, List.of(au1)); Article a2 = new Article("a2", 1992, List.of(au1, au2)); Article a3 = new Article("a3", 1993, List.of(au1, au3, au4)); Article a4 = new Article("a4", 1992, List.of(au1, au2, au3, au4)); List<Article> articles = List.of(a1, a2, a3, a4); BiFunction<Article, Author, Stream<PairOfAuthors>> buildPairOfAuthors = (article, firstAuthor) -> article.authors().stream().flatMap( secondAuthor -> PairOfAuthors.of(firstAuthor, secondAuthor).stream());//Optional的Stream Function<Article, Stream<PairOfAuthors>> toPairOfAuthors = article -> article.authors().stream().flatMap(firstAuthor -> buildPairOfAuthors.apply(article, firstAuthor)); Collector<PairOfAuthors, ?, Map<PairOfAuthors, Long>> collector1 = Collectors.groupingBy(Function.identity(), Collectors.counting());
// System.out.println("numberOfAuthorsTogether=" + numberOfAuthorsTogether); Function<Map<PairOfAuthors, Long>, Map.Entry<PairOfAuthors, Long>> finisher1 = map1 -> map1.entrySet().stream().max(Map.Entry.comparingByValue()).orElseThrow(); Map.Entry<PairOfAuthors, Long> result11 = articles.stream().flatMap(toPairOfAuthors).collect(Collectors.collectingAndThen(collector1, finisher1)); Map.Entry<PairOfAuthors, Long> result12 = articles.stream().collect(Collectors.flatMapping(toPairOfAuthors, Collectors.collectingAndThen(collector1, finisher1))); //找出每年发表文章最多的两位联合作者 Collector<Article, ?, Optional<Map.Entry<PairOfAuthors, Long>>> flatMapping = Collectors.flatMapping(toPairOfAuthors, Collectors.collectingAndThen( collector1, map2 -> map2.entrySet() .stream() .max(Map.Entry.comparingByValue()))); Map<Integer, Optional<Map.Entry<PairOfAuthors, Long>>> result13 = articles.stream().collect(Collectors.groupingBy(Article::inceptionYear, flatMapping)); Map<Integer, Map.Entry<PairOfAuthors, Long>> result14 = result13.entrySet() .stream() .flatMap(entry -> entry.getValue() .map(value -> Map.entry(entry.getKey(), value)) .stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); System.out.println(result11); System.out.println(result12); System.out.println(result13); System.out.println(result14); }}
复制代码


public record Article(String title, int inceptionYear, List<Author> authors) {
}public record Author(String name) implements Comparable<Author> { public int compareTo(Author other) { return this.name.compareTo(other.name); }}
public record PairOfAuthors(Author first, Author second) { public static Optional<PairOfAuthors> of(Author first, Author second) { if (first.compareTo(second) > 0) { return Optional.of(new PairOfAuthors(first, second)); } else { return Optional.empty(); } }}
复制代码


Kotlin


 
复制代码


可以看出:


  • 这个例子主要体现 Optional 跟 StreamAPI 的结合,Kotlin 里没有 Optional,所以很难写出。


这局 Java 胜。

最终

Kotlin 以 2:1 微弱优势胜出。


Java 用他的严谨,证明能实现从简单到复杂的各种场景。


Kotlin 用它的简洁,通常情况下能减少工作量。


Kotlin 还提供了委托、扩展、运算符重载、作用域函数、协程等等。


子曾经曰过”越简洁,越有坑“。想完全用 Kotlin 取代 Java,还有一段路,目前二者可以互操作,建议同时使用。


复杂场景下,用什么语言并不是决定性的,解决方案才是。

发布于: 2023-07-15阅读数: 22
用户头像

烧霞

关注

还未添加个人签名 2020-08-26 加入

一步一步 架构师之路

评论

发布
暂无评论
从头学Java17-今天的Kotlin更香吗_kotlin_烧霞_InfoQ写作社区