C 系列的编程语言有标准的声明类型的方法。简而言之list初始化,首先指定一个类型,然后是该符合类型的东西,比如变量、字段、方法等等。

Java 中的表示方法是:

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

Kotlin 中则是:

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

这种方法有几个原因令人讨厌。

首先,你需要在名称和类型之间加入这个多余的冒号。这个额外角色的目的是什么?为什么名称与其类型要分离?我不知道。可悲的是,这让你在 Kotlin 的工作变得更加困难。

第二个问题,当你读取一个方法声明时,你首先看到的是名字和返回类型,然后才是参数。

在 Kotlin 中,方法的返回类型可能远在行尾,所以需要浏览很多代码才能看到:

private fun getMetricValue(kafkaTemplate : KafkaTemplate<String, ByteArray>, 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<String>) {
            SpringApplication.run(AppRunner::class.java*args)
        }
    }
}

集合字面量

在Java中,初始化列表非常繁琐:

import java.util.Arrays;
...
List<String> strings = Arrays.asList("Saab""Volvo");

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

import com.google.common.collect.ImmutableMap;
...
Map<StringString> 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 的这篇文章:)。

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 安全list初始化,你必须在每个代码之前添加 let:

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

现在,比较 Java 和 Kotlin 版本的可读性。你更倾向哪个?

数据类

数据类是 Kotlin 在实现 Value Objects 时使用的方法,以减少 Java 中不可避免的样板问题。

例如,在 Kotlin 中,你只写一个 Value Object :

data class User(val name: String, val age: Int)

Kotlin 对 equals()、hashCode()、toString() 以及 copy() 有很好的实现。在实现简单的DTO 时它非常有用。但请记住,数据类带有严重的局限性。你无法扩展数据类或者将其抽象化,所以你可能不会在核心模型中使用它们。

这个限制不是 Kotlin 的错。在 equals() 没有违反 Liskov 原则的情况下,没有办法产生正确的基于价值的数据。

这也是为什么 Kotlin 不允许数据类继承的原因。

开放类

Kotlin 类默认为 final。如果你想扩展一个类,必须添加 open 修饰符。

继承语法如下所示:

open class Base
class Derived : Base()

Kotlin 将 extends 关键字更改为: 运算符,该运算符用于将变量名称与其类型分开。那么再回到 C ++语法?对我来说这很混乱。

这里有争议的是,默认情况下类是 final。也许 Java 程序员过度使用继承,也许应该在考虑扩展类之前考虑三次。但我们生活在框架世界,Spring 使用 cglib、jassist 库为你的 bean 生成动态代理。Hibernate 扩展你的实体以启用延迟加载。

如果你使用 Spring,你有两种选择。你可以在所有 bean 类的前面添加 open,或者使用这个编译器插件:

buildscript {
    dependencies {
        classpath group: 'org.jetbrains.kotlin', name: 'kotlin-allopen', version: "$versions.kotlin"
    }
}

陡峭的学习曲线

如果你认为自己有 Java 基础就可以快速学习 Kotlin,那你就错了。Kotlin 会让你陷入深渊,事实上,Kotlin 的语法更接近 Scala。这是一项赌注,你将不得不忘记 Java 并切换到完全不同的语言。

相反,学习 Groovy 是一个愉快的过程。Java 代码是正确的 Groovy 代码,因此你可以通过将文件扩展名从 .java 更改为 .groovy。

最后的想法

学习新技术就像一项投资。我们投入时间,新技术让我们得到回报。但我并不是说 Kotlin 是一种糟糕的语言,只是在我们的案例中,成本远超收益。

以上内容编译自 From Java to Kotlin and Back Again,作者 Kotlin ketckup。

list初始化_list list as item_list嵌套list 导出xml

他是一名具有15年以上专业经验的软件工程师,专注于JVM 。在 Allegro,他是一名开发团队负责人,JaVers 项目负责人,Spock 倡导者。此外,他还是 allegro.tech/blog 的主编。

本文一出就引发了业内的广泛争议,Kotlin 语言拥护者Márton Braun 就表示了强烈的反对。

Márton Braun 十分喜欢 Kotlin 编程,目前他在 StackOverflow 上 Kotlin 标签的最高用户列表中排名第三,并且是两个开源 Kotlin 库的创建者,最著名的是 MaterialDrawerKt。此外他还是 Autosoft 的 Android 开发人员,目前正在布达佩斯技术经济大学攻读计算机工程硕士学位。

以下就是他针对上文的反驳:

当我第一次看到这篇文章时,我就想把它转发出来看看大家会怎么想,我肯定它会是一个有争议的话题。后来我读了这篇文章,果然证明了它是一种主观的、不真实的、甚至有些居高临下的偏见。

有些人已经在原贴下进行了合理的批评,对此我也想表达一下自己的看法。

名称遮蔽

“IDEA 团队”(或者 Kotlin 插件团队)和“Kotlin 团队”肯定是同样的人,我从不认为内部冲突会是个好事。语言提供这个功能给你,你需要的话就使用,如果讨厌,调整检查设置就是了。

类型推断

Kotlin 的类型推断无处不在,作者说的 Java 10 同样可以简直是在开玩笑。

Kotlin 的方式超越了推断局部变量类型或返回表达式体的函数类型。这里介绍的这两个例子是那些刚刚看过关于 Kotlin 的第一次介绍性讲话的人会提到的,而不是那些花了半年学习该语言的人。

例如,你怎么能不提 Kotlin 推断泛型类型参数的方式?这不是 Kotlin 的一次性功能,它深深融入了整个语言。

编译时 Null 安全

这个批评是对的,当你与 Java 代码进行互操作时,Null 安全性确实被破坏了。该语言背后的团队曾多次声明,他们最初试图使 Java 可为空的每种类型,但他们发现它实际上让代码变得更糟糕。

Kotlin 不比 Java 更差,你只需要注意使用给定库的方式,就像在 Java 中使用它一样,因为它并没有不去考虑 Null 安全。如果 Java 库关心 Null安全性,则它们会有许多支持注释可供添加。

也许可以添加一个编译器标志,使每种 Java 类型都可以为空,但这对 Kotlin 团队来说不得不花费大量额外资源。

类名称字面常量

:: class 为你提供了一个 KClass 实例,以便与 Kotlin 自己的反射 API 一起使用,而:: class.java为你提供了用于 Java 反射的常规 Java 类实例。

限时特惠:本站每日持续更新海量设计资源,一年会员只需29.9元,全站资源免费下载
站长微信:ziyuanshu688