栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Kotlin只是一个“网红”而已,Java才是世界上最好的语言

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Kotlin只是一个“网红”而已,Java才是世界上最好的语言

实话实说,Kotlin 在这一点上确实更胜一筹。当然,类型推断还可应用在多个场景。关于 Java 10中的局部变量类型推断,点击以下链接了解更多:

  • https://medium.com/@afinlay/java-10-sneak-peek-local-variable-type-inference-var-3022016e1a2b

Null 安全类型
Null 安全类型是 Kotlin 的杀手级功能。这个想法很好,在 Kotlin 中,类型默认不可为空。如果你需要添加一个可为空的类型,可以像下列代码这样:

val a: String? = null // ok
val b: String = null // compilation error

假设你使用了可为空的变量但是并未进行空值检查,这在 Kotlin 将无法通过编译,比如:

println (a.length) // compilation error
println (a?.length) // fine, prints null
println (a?.length ?: 0) // fine, prints 0

那么是不是如果你同时拥有不可为空和可为空的变量,就可以避免 Java 中最常见的 NullPointerException 异常吗?事实并没有想象的简单。当 Kotlin 代码必须调用 Java 代码时,事情会变得很糟糕,比如库是用 Java 编写的,我相信这种情况很常见。于是第三种类型产生了,它被称为平台类型。Kotlin 无法表示这种奇怪的类型,它只能从 Java 类型推断出来。它可能会误导你,因为它对空值很宽松,并且会禁用 Kotlin 的 NULL 安全机制。看看下面这个 Java 方法:

public class Utils {
static String format(String text) {
r

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

浏览器打开:qq.cn.hn/FTf 免费领取

eturn text.isEmpty() ? null : text;
}
}

假如你想调用 format(String)。应该使用哪种类型来获得这个 Java 方法的结果呢?你有三个选择。第一种方法:你可以使用 String,代码看起来很安全,但是会抛出 NullPointerException 异常。

fun doSth(text: String) {
val f: String = Utils.format(text) // compiles but assignment can throw NPE at runtime
println ("f.len : " + f.length)
}

那你就需要用 Elvis 来解决这个问题:

fun doSth(text: String) {
val f: String = Utils.format(text) ?: “” // safe with Elvis
println ("f.len : " + f.length)
}

第二种方法:你可以使用 String,能够保证 Null 安全性。

fun doSth(text: String) {
val f: String? = Utils.format(text) // safe
println ("f.len : " + f.length) // compilation error, fine
println ("f.len : " + f?.length) // null-safe with ? operator
}

第三种方法:让 Kotlin 做局部变量类型推断如何?

fun doSth(text: String) {
val f = Utils.format(text) // f type inferred as String!
println ("f.len : " + f.length) // compiles but can throw NPE at runtime
}

馊主意!这个 Kotlin 代码看起来很安全、可编译,但是它容忍了空值,就像在 Java 中一样。除此之外,还有另外一个方法,就是强制将 f 类型推断为 String:

fun doSth(text: String) {
val f = Utils.format(text)!! // throws NPE when format() returns null
println ("f.len : " + f.length)
}

在我看来,Kotlin 的所有这些类似 scala 的类型系统过于复杂。Java 互操作性似乎损害了 Kotlin 类型推断这个重量级功能。类名称字面常量使用类似 Log4j 或者 Gson 的 Java 库时,类文字很常见。Java 使用 .class 后缀编写类名:

Gson gson = new GsonBuilder().registerTypeAdapter(LocalDate.class, new LocalDateAdapter()).create();

Groovy 把类进行了进一步的简化。你可以忽略 .class,它是 Groovy 或者 Java 类并不重要。

def gson = new GsonBuilder().registerTypeAdapter(LocalDate, new LocalDateAdapter()).create()

Kotlin 把 Kotlin 类和 Java 类进行了区分,并为其提供了语法规范:

val kotlinClass : KClass = LocalDate::class
val javaClass : Class = LocalDate::class.java

因此在 Kotlin 中,你必须写成如下形式:

val gson = GsonBuilder().registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()).create()

这看起来非常丑陋。反向类型声明C 系列的编程语言有标准的声明类型的方法。简而言之,首先指定一个类型,然后是该符合类型的东西,比如变量、字段、方法等等。Java 中的表示方法是:

int inc(int i) {
return i + 1;
}

Kotlin 中则是:

fun inc(i: Int): Int {
return i + 1
}

这种方法有几个原因令人讨厌。首先,你需要在名称和类型之间加入这个多余的冒号。这个额外角色的目的是什么?为什么名称与其类型要分离?我不知道。可悲的是,这让你在 Kotlin 的工作变得更加困难。第二个问题,当你读取一个方法声明时,你首先看到的是名字和返回类型,然后才是参数。在 Kotlin 中,方法的返回类型可能远在行尾,所以需要浏览很多代码才能看到:

private fun getMetricValue(kafkaTemplate : KafkaTemplate, metricName : String) : Double {

}

或者,如果参数是逐行格式的,则需要搜索。那么我们需要多少时间才能找到此方法的返回类型呢?

@Bean
fun kafkaTemplate(
@Value("${interactions.kafka.bootstrap-servers-dc1}") bootstrapServersDc1: String,
@Value("${interactions.kafka.bootstrap-servers-dc2}") bootstrapServersDc2: String,
cloudmetadata: Cloudmetadata,
@Value("${interactions.kafka.batch-size}") batchSize: Int,
@Value("${interactions.kafka.linger-ms}") lingerMs: Int,
metricRegistry : MetricRegistry
): KafkaTemplate {
val bootstrapServer = if (cloudmetadata.datacenter == “dc1”) {
bootstrapServersDc1
}

}

第三个问题是 IDE 中的自动化支持不够好。标准做法从类型名称开始,并且很容易找到类型。一旦选择一个类型,IDE 会提供一些关于变量名的建议,这些变量名是从选定的类型派生的,因此你可以快速输入这样的变量:

MongoExperimentsRepository repository

Kotlin 尽管有 IntelliJ 这样强大的 IDE,输入变量仍然是很难的。如果你有多个存储库,在列表中很难实现正确的自动补全,这意味着你不得不手动输入完整的变量名称。

repository : MongoExperimentsRepository

伴生对象一位 Java 程序员来到 Kotlin 面前。

“嗨,Kotlin。我是新来的,我可以使用静态成员吗?"他问。 “不行。我是面向对象的,静态成员不是面向对象的。” Kotlin 回答。 “好吧,但我需要 MyClass 的 logger,我该怎么办?” “这个没问题,使用伴生对象即可。” “那是什么东西?” “这是局限到你的类的单独对象。把你的 logger 放在伴生对象中。”Kotlin解释说。 “我懂了。这样对吗?”

class MyClass {
companion object {
val logger = LoggerFactory.getLogger(MyClass::class.java)
}
}

“正确!” “很详细的语法,”程序员看起来很疑惑,“但是没关系,现在我可以像 MyClass.logger 这样调用我的 logger,就像 Java 中的一个静态成员?” “嗯…是的,但它不是静态成员!这里只有对象。把它看作是已经实例化为单例的匿名内部类。事实上,这个类并不是匿名的,它的名字是 Companion,但你可以省略这个名字。看到了吗?这很简单。"

我很欣赏对象声明的概念——单例很有用。但从语言中删除静态成员是不切实际的。在 Java 中我们使用静态 Logger 很经典,它只是一个 Logger,所以我们不关心面向对象的纯度。它能够工作,从来没有任何坏处。因为有时候你必须使用静态。旧版本 public static void main() 仍然是启动 Java 应用程序的唯一方式。

class AppRunner {
companion object {
@JvmStatic fun main(args: Array) {
SpringApplication.run(AppRunner::class.java, *args)
}
}
}

集合字面量在Java中,初始化列表非常繁琐:

import java.util.Arrays;

List strings = Arrays.asList(“Saab”, “Volvo”);

初始化地图非常冗长,很多人使用 Guava:

import com.google.common.collect.ImmutableMap;

Map string = ImmutableMap.of(“firstName”, “John”, “lastName”, “Doe”);

在 Java 中,我们仍然在等待新的语法来表达集合和映射。语法在许多语言中非常自然和方便。
Javascript:

const list = [‘Saab’, ‘Volvo’]
const map = {‘firstName’: ‘John’, ‘lastName’ : ‘Doe’}

Python:

list = [‘Saab’, ‘Volvo’]
map = {‘firstName’: ‘John’, ‘lastName’: ‘Doe’}

Groovy:

def list = [‘Saab’, ‘Volvo’]
def map = [‘firstName’: ‘John’, ‘lastName’: ‘Doe’]

简单来说,集合字面量的整齐语法就是你对现代编程语言的期望,特别是如果它是从头开始创建的。Kotlin 提供了一系列内置函数,比如 listOf()、mutableListOf()、mapOf()、hashMapOf() 等等。
Kotlin:

val list = listOf(“Saab”, “Volvo”)
val map = mapOf(“firstName” to “John”, “lastName” to “Doe”)

在地图中,键和值与 to 运算符配对,这很好。但为什么一直没有得到广泛使用呢?令人失望。Maybe函数式语言(比如 Haskell)没有空值。相反,他们提供 Maybe monad(如果你不熟悉monad,请阅读 Tomasz Nurkiewicz 的这篇文章:http://www.nurkiewicz.com/2016/06/functor-and-monad-examples-in-plain-java.html)。Maybe 很久以前就被 Scala 以 Option 引入到 JVM 世界,然后在 Java 8 中被采用为 Optional。如今,Optional 是在 API 边界处理返回类型中的空值的非常流行的方式。Kotlin 中没有 Optional 的等价物,所以你大概应该使用 Kotlin 的可空类型。让我们来调查一下这个问题。通常情况下,当你有一个 Optional 的时候,你想要应用一系列无效的转换。例如,在 Java 中:

public int parseAndInc(String number) {
return Optional.ofNullable(number)
.map(Integer::parseInt)
.map(it -> it + 1)
.orElse(0);
}

在 Kotlin 中,为了映射你可以使用 let 函数:

fun parseAndInc(number: String?): Int {
return number.let { Integer.parseInt(it) }
.let { it -> it + 1 } ?: 0
}

上面的代码是错误的,parseInt() 会抛出 NPE 。map() 仅在有值时执行。否则,Null 就会跳过,这就是为什么 map() 如此方便。不幸的是,Kotlin 的 let 不会那样工作。它从左侧的所有内容中调用,包括空值。为了保证这个代码 Null 安全,你必须在每个代码之前添加 let:

fun parseAndInc(number: String?): Int {
return number?.let { Integer.parseInt(it) }
?.let { it -> it + 1 } ?: 0
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/445100.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号