跳到内容

科特林教程– Quick Reference –Kotlin入门

312018年1月 通过 s1m0nw1 5条留言

介绍

免责声明: 该参考文献最初以 DZone Refcard.

科特林 在过去的几个月中,它已经成为最受欢迎的JVM语言之一。一个特殊的原因是,在Google将Kotlin设为Android开发的官方语言之后,它在Android社区中引起了广泛关注。 科特林由 JetBrains,负责最著名的IDE。 IntelliJ IDEA。不过,这是一种开源语言,可以在 的GitHub.

据说这种语言非常 简洁, 安全 就错误发生频率而言,它可以与Java互操作,并且还提供了许多功能,可以进行功能编程,编写类型安全的DSL等。除了JVM外,Kotlin还可以针对大多数Android版本进行编译,甚至可以使用LLVM编译为机器代码,还可以将其编译为JavaScript。
科特林已经被许多流行的框架和工具所采用,例如Spring和Gradle。它继续在多个领域获得关注,并且从来没有比现在更好的时机了。 科特林入门.

从哪里开始编码

当您想开始编写第一个Kotlin代码时,有很多方法可以做到这一点。显然,推荐的方法是与IntelliJ IDEA一起使用,后者可提供最佳支持。或者,也可以从命令行开始,或者使用JetBrains的Kotlin Web IDE来做一些Kotlin Koans。无论您喜欢哪种方式,都可以在以下位置找到相应的教程:kotlinlang.org/docs/tutorials/。

基本语法

科特林 was inspired 通过 many modern programming languages like C#, Groovy, Scala 并且 Java. Even more, 科特林 can be seen as an 延期 to the Java language, making it better 通过 adding functionality to existing standard 类es (e.g. String, List) and of course 通过 providing great features, which are in large part enabled 通过 applying compiler-supported techniques. As in Java, 科特林 programs are entered via a main method, such as the following:

fun main(args: Array): Unit {
    val inserted = "科特林"
    println("Let's get started with $inserted")
}

我们可以在此片段中看到的是:

  • 职能 are initiated 通过 the keyword fun, followed 通过 a name
  • 参量 并且 变数 您可以在Kotlin中通过定义名称和类型来声明它们,两者之间都用冒号分隔 args: Array
  • 的 return type of the main is Unit, also prefaced 通过 a colon. In case of a Unit return, which corresponds to Java's void, the compiler does not require you to explicitly define the return type, so the part : Unit could be omitted
  • 科特林不需要您使用 分号 用于分隔语句(在大多数情况下)
  • 类型推断 is supported in many situations as shown with val inserted, which also could be declared with an explicit type as val inserted: String
  • 字符串模板 can be used, which means that it's possible to include 变数 and even 表达 in Strings directly using $varname or ${statement} syntax
  • main is declared without a wrapping around it. 职能 and 变数 in 科特林 may be declared at "top-level", i.e directly inside a package
  • 没有 可见性修改器 is used here. 职能, 类es, 变数 etc. are public 通过 default. When different visibility is needed, choose from:
关键词对顶层声明的影响 [1]对班级成员的影响
public随处可见如果可以访问课程,则在任何地方都可见
private仅在文件内可见仅在班级内可见
protected-在类和子类中可见
internal在同一模块内可见 [2]如果可访问类,则在同一模块中可见

1:可以在“顶层”上声明函数,属性和类,对象和接口
2:一个模块是一起编译的一组Kotlin文件:一个IntelliJ IDEA模块,一个Maven项目,一个Gradle源集

  • Variables defined as val cannot be re-assigned, i.e. are read-only. Alternatively, if mutability is inevitable, var can be utilized, as shown in the next example:
var 易变的Var = StringBuilder("first")
mutableVar = StringBuilder("second")
  • Constructor is invoked without the new keyword, which is omitted from kotlin

控制流:条件

In 科特林 you can make use of if, when, for and while for controlling the behavior of your code. Let's look at conditions first.

如果陈述

val min: Int
if (x < y) {
    min = x
} else {
    min = y
}

重要的是要知道 陈述 在科特林还可以用作 表达,例如,这会使三元运算符过时,并且在大多数情况下显然会缩短代码:

val min = if (x < y) x else y 

When-Statement A when statement is very similar to switch operators and could, in theory, easily replace if-statements as they are much more powerful.

val y = when (x) { 
    0 -> "is zero"
    1 -> "is one"
    2, 3 -> "two or three"
    is Int -> "is Int"
    is Double -> "is Double"
    in 0..100 -> "between 0 and 100"
    else -> "else block"
}

In a when statement, which can also be used as an expression, all branches are tried to match the input until one condition is satisfied. If no branch matches, the else is executed. As shown in the snippet, when branch conditions can be values, types, 范围s and more.

控制流:循环

前循环

正如您从C或Java所知,在Kotlin中,没有常规的for循环。代替, 前言 循环是默认设置。

for (c in "charSequence") {
    println(c)
}

In many cases, looping with an index is necessary, which can easily be achieved with the indices property that is defined for arrays, lists 并且 CharSequences for example.

for (i in "charSequence".indices) {
    println("charSequence"[i])
}

Another way of iterating with indices is possible 通过 using withIndix().

for ((i,c) in "charSequence".withIndex()) {
    println("$i: $c")
}

最后但并非最不重要的一点是,Kotlin具有范围,也可以将其用于索引迭代,如下所示:

(0 .. "charSequence".length-1).forEach {
    print("charSequence"[it])
}

的 范围 in this example is expressed with the common .. syntax. To create a 范围 which does not include the end element (s.length), the until function is used: (0 until s.length).

循环时

Constructs with while or do-while loops are straight-forward, all works as known from other common languages.

基本类型

在Kotlin中,即使是原始类型,所有内容对于用户来说都看起来像一个对象。这意味着,可以在每种类型上调用成员函数,尽管有些将表示为 JVM原语 在运行时。

号码

的 default number types are: Double, Float, Long, Int, Short, Byte
*下划线可用于使大量数字更具可读性: val million = 1_000_000
* Number types offer conversion methods like toByte(): Byte, toInt(): Int , toLong(): Long
* 科特林中的字符不是数字类型

字符

A Char represents characters and cannot be treated as a number.
*它们用单引号声明,例如 '42'
* An explicit conversion from a Char to an Int can be accomplished with the toInt() method

布尔值

布尔值 can have the two common values true and false
* 的y can be operated on with: ||, &amp;&amp; and !

弦乐

字符串是字符的不可变序列。
* 的y offer an index operator [] for accessing characters at specified positions
* A string literal in 科特林 looks like "Hello World" or """Hello World with "another String" in it"""
*后者称为 生的 可以包含任何字符而无需转义特殊符号的字符串
* Strings in 科特林 may contain template 表达

数组

An array is represented 通过 the 类 Array, which offers very useful methods to the client.
* Values can be obtained via get(index) or [index]
* Values can be set via set(index, value) or [index]=value
* 数组 are invariant, i.e. an Array cannot be assigned to a variable of type Array
* Special types for arrays of primitive types exist as IntArray or ShortArray for instance. Using those will reduce the boxing overhead.

班级

可以像下面的代码段一样声明一个简单的类:

类 Person constructor(name: String) {}

的 primary constructor is part of the 类 header, secondary constructors can be added in the 类 body. In the shown case, the constructor keyword could also be omitted, since it's 只要 mandatory if you want to add annotations or 可见性修改器s (default: public).
Constructor parameters such as name can be used during the initialization of an object of this 类. For this purpose, an init block would be necessary, because primary constructors can't contain code directly. Constructor arguments can also be used in property initializers that are declared in the 类 body, as shown here.

类 Person(name: String, age: Int) {
    init {
        println("new Person $name will be born.")
    }

    val ageProp = age
}

As mentioned, 科特林 类es can contain 属性, which are accessed 通过 simply calling obj.propertyName to get a property's value and obj.propertyName = "newValue" to modify the value of 易变的 (var) property. Declaring 属性 for 类es can also be done in the primary constructor directly, which makes the code even more 简洁. Like in all methods, 科特林 supports 默认参数 for parameters, set with "=".

类 Person(val name: String, val age: Int = 50)

Same as with local 变数, instead of val, a property can be declared 易变的 using var instead. 没有te that you don't have to write an empty 类 body if no content is defined.

特别班

除了普通的类,Kotlin还知道一些特殊的类声明,这是值得了解的。以下将快速概述。

类型说明
数据Adds standard functionality for toString, equals, hashCode etc.
sealed将类层次结构限制为一组子类型。有用的 when
嵌套类可以在其他类(也称为“内部类”)中创建类
enum收集可以包含逻辑的常量
object 声明书用于创建类型的单例

Of course, 科特林 also supports inheritance through interfaces and abstract 类es.

函数类型和Lambda

In order to be able to understand idiomatic 科特林 code, it's essential to recognize how function types and especially lambdas look like. Just as you can declare 变数 of type Int or String, it's also possible to declare 变数 of function types, e.g. (String) -> Boolean.

val myFunction: (String) -> Boolean = { s -> s.length > 3 }
myFunction("HelloWorld")

的 variable is declared as a function type that takes a String argument and returns a Boolean. 的 method itself is defined as a lambda enclosed in curly braces. In the shown lambda, the String parameter is declared and named before the -> symbol, whereas the body follows 后 it.

Lambda特殊语法

语言设计师决定了一些特殊的lambda功能,这些功能使用法更加强大。

  1. it:单个参数的隐式名称

In many cases, lambdas are used with single parameters like in the previous example. In such situations, you don't have to give the parameter an explicit name. Instead, the implicit name it can be used.

val myFunction: (String) -> Boolean = { it.length > 3 }
myFunction("HelloWorld")
  1. 对于未使用的参数,请使用 _

在某些情况下,可能不必利用lambda中的每个可能的可用参数。编译器会警告开发人员此类未使用的变量,可以通过使用 下划线.

val myFunction: (String, Int) -> Boolean = { s, _ -> s.length > 3 }
myFunction("HelloWorld", 42)

高阶函数

如果一个函数将另一个函数作为参数或返回另一个函数作为结果,则称为 高阶函数。这些功能在Kotlin中是必不可少的,因为许多库功能都依赖于此概念。让我们来看一个例子。

fun main(args: Array) {
    myHigherOrderFun(2, { it.length > 2 })
}

fun myHigherOrderFun(iterations: Int, test: (String) -> Boolean){
    (0 until iterations).forEach {
        println("$it: ${test("myTestString")}")
    }
}

的 function myHigherOrderFun defines two parameters, one of which is another function test. 的 function takes test and applies a String to it multiple times depending on what the first argument iterations is. By the way, the example uses a 范围 to imitate an indexed for loop here.

的 shown main function demonstrates the usage of a 高阶函数 通过 calling it with an anonymous function. 的 syntax looks a bit messy, which is why the language designers decided on a very important convention: If a lambda is the 持续 函数的参数,可以放置 右括号,或者如果是 只要 argument, the parentheses can be omitted completely like shown with forEach above. 的 following snippet demonstrates this convention applied to an invocation of myHigherOrderFun.

//Lambda 后 closing parentheses
myHigherOrderFun(2) {
    it.length>2
}

主要功能

科特林有一些功能,每个人都应该熟悉。这些对于许多库,标准功能以及高级功能(如 领域特定语言 支持。

空安全

的 type system differentiates between nullable and non-null types. By default, a 类 like String cannot reference null, which raises the attention for null-related problems. As opposed to String, the type String? can hold null. This does not make a big difference on its own. 的refore, working with nullable types implies having to handle nullable values in a special way.

var b: String? = "couldBeNull"
b = null //okay

// 1. Access directly: does not compile, could throw NPE
// val len = b.length

//2. 采用 安全-operator
val len = b?.length

//3. Check nullability before accessing
if(b != null){
    b.length
}

It's possible to check whether a variable is not null before accessing it. In such cases, the compiler permits the usage without special 安全ty measures. Alternatively, b?.length expresses: call length on b if it's not null, otherwise the expression returns null. 的 return is of type Int? because null may be returned. Chaining such calls is possible, which is very useful. Other operators used with nullable types are shown in the following overview.

操作员用例
!!忽略编译器警告并克服空检查。仅谨慎使用。x!!.length
?: 猫王算子 is used to give an alternative for null results.val len: Int = b?.length ?: 0
如?A 安全投 tries to cast a variable in a type and results in null if the cast is not possible.val i: Int? = s 如? Int

扩展名

科特林的另一个基本功能是 扩展名. An 延期 is used to extend a 类 with new functionality without having to inherit from that 类. 扩展名 can have the form of 属性 and functions. 的 科特林 standard library contains a lot of such 扩展名, like the following defined on String:

public fun String.substring(range: IntRange): String = 
    substring(range.start, 范围.endInclusive + 1)

//usage
"myString".substring(0..3)

In this example String is the 接收者 of the defined substring(range: IntRange) function. An 延期 function can use visible members of its 接收者 without additional qualifiers since this refers to the 接收者. In the snippet, String's standard method substring(startIndex: Int, endIndex: Int) is called in that way. 的 延期 is called on a String as if it was a regular method.

也可以用 属性. For example, Int can be extended with a property that represents its version of BigDecimal. This might be useful if otherwise, the constructor of BigDecimal had to be used many times.

val Int.bd
    get() = BigDecimal(this)

val bd: BigDecimal = 5.bd

扩展名大多在顶级定义,并且在明确导入后可以在其他文件中使用。

带接收器的Lambda

如果与“带有接收器的lambda”一起使用,高阶函数甚至会更强大。与扩展函数类似,可以使用特定的接收器对象来调用函数文字。因此,可以直接在lambda内部访问接收方的成员,而不必使用其他限定符。此功能是Kotlin出色的写作支持的基础 类型安全的建筑商也称为 领域特定语言.

fun T.apply(block: T.() -> Unit): T {
    block()
    return this
}

This snippet shows a slightly simplified version of the apply function, which is part of 科特林's standard library. It's an 延期 function on the generic type T, thus can be used with any object. 的 function takes a function literal with T as its 接收者 and executes the block before this (the 接收者 of apply) is being returned.

数据 类 GuiContainer(
    var width: Int = 0,
    var height: Int = 0,
    var background: String = "red"
) {
    fun printMe() = println(this)
}

fun main(args: Array) {
    val container = GuiContainer().apply {
        width = 10
        height = 20
        background = "blueish"
        printMe()
    }
}

In this example, the 数据 类 GuiContainer is created with 默认参数 and then the apply method is called on it. It's possible to set 易变的 属性 and call methods of the 接收者 GuiContainer like shown with the invocation of printMe() in the end. Since apply returns the 接收者 后 it completes, it can directly be assigned to a variable.

惯用科特林

科特林试图鼓励特殊 编码习语 要使用的。这些是部分 列出的 在文档以及一些社区驱动的文章中。以下将通过示例介绍其中一些惯用法。

  1. 采用 when as an expression if possible
fun analyzeType(obj: Any) =
        when(obj){
            is String -> "is String"
            else -> "no String"
        }
  1. 采用 猫王 operator with throw and return to handle nullable values

类 Person(val name: String?, val age: Int?) fun process(person: Person) { val pName = person.name ?: throw IllegalArgumentException("Name must be provided.") println("processing $pName") val pAge = person.age ?: return println("$pName is $pAge years old") }
  1. 利用 范围 支票
fun inLatinAlphabet(char: Char) = char in 'A'..'Z'
  1. 更喜欢 默认参数 过载
fun greet(person: Person, printAge: Boolean = false) {
    println("Hello ${person.name}")
    if (printAge)
      println("${person.name} is ${person.age} years old")
}
  1. 采用 输入别名 对于功能类型
typealias StringPredicate = (String) -> Boolean

val pred: StringPredicate = {it.length > 3}
  1. 采用 数据 多个返回值的类
数据 类 Multi(val s: String, val i: Int)

fun foo() = Multi("one", 1)

fun main(args: Array){
    val (name, num) = foo()
}
  1. 更喜欢 延期 函数到实用程序样式的函数
fun Person.greet(printAge: Boolean = false) {
    println("Hello $name")
    if (printAge)
        println("$name is $age years old")
}
  1. 采用 apply for object initialization
数据 类 GuiContainer(
    var width: Int = 0,
    var height: Int = 0,
    var background: String = "red"
) {
    fun printMe() = println(this)
}

fun main(args: Array) {
    val container = GuiContainer().apply {
        width = 10
        height = 20
        background = "blueish"
        printMe()
    }
}

  1. 采用 compareBy for complex comparisons
fun sort(persons: List): List =
    persons.sortedWith(compareBy(Person::name, Person::age))
  1. 采用 mapNotNull to combine map and filter for non-null values
fun getPersonNames(persons: List): List =
    persons.mapNotNull { it.name }
  1. 采用 object to apply Singleton pattern
object PersonRepository{
    fun save(p: Person){}
    //...
}

//usage
val p = Person("Paul", 40)
PersonRepository.save(p)
  1. 不要利用 !!
//Do not use !!, there's always a better solution
person!!.address!!.street!!.length
  1. 首选只读数据结构
//Whenever possible, do not use 易变的 Data Structures

val 易变的List: MutableList = 易变的ListOf(1, 2, 3)
mutableList[0] = 0

val readOnly: List = listOf(1, 2, 3)
readOnly[0] = 0 // Does not compile
  1. 采用 let to execute code if 接收者 is 不为空
fun letPerson(p: Person?) {
    p?.let {
        println("Person is 不为空")
    }
}

资源资源

语言参考:
官方参考文件: //kotlinlang.org/docs/reference/
的GitHub存储库: //github.com/JetBrains/kotlin
工具和框架集合: //kotlin.link
运算符和关键字概述: //kotlinlang.org/docs/reference/keyword-reference.html

社区:
松弛: //kotlinlang.slack.com
推特: //twitter.com/kotlin
通讯: http://kotlinweekly.net
讨论: //discuss.kotlinlang.org

博客:
JetBrains: //blog.jetbrains.com/kotlin/
西蒙·维尔茨: //kotlinexpertise.com

其他:
行动书中的科特琳: 行动中的科特林
图书: //kotlinlang.org/docs/books.html
在线IDE: //try.kotlinlang.org

5个想法“科特林教程– Quick Reference –Kotlin入门

发表评论

您的电子邮件地址不会被公开。 必需的地方已做标记 *