耶耶耶耶耶 发表于 2023-7-15 16:56:09

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

出于各种限制,很多公司依然停留在Java8,部分小伙伴转向了Kotlin。Kotlin作为静态编译语言,提供大量语法糖,而且编译后的字节码跟Java一致。
当时,Java8于2014年发布,Kotlin于2016年,很多宣称的语法糖都是对比的Java8。不禁要问,相对今天的Java17,Kotlin优势还在吗?
现在就用最新的Kotlin1.9.0,对前三篇文章里的lambda、StreamAPI依次改造,实践出真知!
https://i.hongkj.cn/java17/kotlin.png/s
编写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
@Builder
public 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.Logger
import Kotlin.math.sqrt
import red.burn.bean.UserKT
import 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()
[*]函数很多有混淆,像groupBy、groupingBy,maxBy、maxOf
[*]不过中间可能有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,还有一段路,目前二者可以互操作,建议同时使用。
复杂场景下,用什么语言并不是决定性的,解决方案才是。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 从头学Java17-今天的Kotlin更香吗