定义方法的方式

fun sum(a: Int, b: Int): Int { return a+b }

fun sum(a: Int, b: Int): Int = a+b fun sum(a: Int, b: Int) = a+b

进一步延伸,

fun transform(color: String): Int = when (color) { "Red" -> 0 "Green" -> 1 "Blue" -> 2 else -> -1 }

默认值

fun foo(a: Int = 0, b: String = "") { ... }

Unit 等于java中的null Any 等于java中的object

?操作符,对于可能为null的需要明确的标识出来。 fun parseInt(str: String): Int? { // ... }

is

is操作符检测对象是否数据一个类型,如果被确认为一个类型,则不需要显示的装换了。 fun getStringLength(obj: Any): Int? { if (obj is String) { return obj.length } return null }

when

when 对应java的switch fun describe(obj: Any): String = when (obj) { 1 -> "One" "Hello" -> "Greeting" is Long -> "Long" !is String -> "Not a string" else -> "Unknown" }

in

in在某某范围内 for (i in 1..100) { ... } for (i in 1 until 100) { ... } for (x in 2..10 step 2) { ... } for (x in 10 downTo 1) { ... } if (x in 1..10) { ... }

过滤

val positives = list.filter { it > 0 }

遍历map

val map = mapOf("a" to 1, "b" to 2, "c" to 3)

println(map["key"])

map["key"] = value

for ((k, v) in map) { println("$k -> $v") }

字符串中带变量

println("Name $name")

lazy延时加载

val p: String by lazy { // compute the string }

单例

object R { val name = "" }

不为空的判断

val files = File("Test").listFiles()

不为空输出

println(files?.size)

不为空XX,否则XX

println(files?.size ?: "empty")

不为空执行

data?.let { transformData(it) } ?: defaultValueIfDataIsNull

try...catch

val result = try { count() } catch (e: ArithmeticException) { throw IllegalStateException(e) }

with对个方法的集合操作

var a: String = "" with(a) { a = 10 print(a) a = 0 }

创建DTO

data class Customer(val name: String, val email: String)

数据类,默认提供了getters和setters、equals、hashCode、toString、copy等

可以带缺省值

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

copy

数据部分改变 val jack = User(name = "Jack", age = 1) val olderJack = jack.copy(age = 2)

反解析DTO数据,从而可以单独使用

val jane = User("Jane", 35) val (name, age) = jane println("$name, $age years of age") // prints "Jane, 35 years of age"

更好的阅读性

val creditCardNumber = 1234_5678_9012_3456L

Array

创建Array的一种方式: val asc = Array(5, { i -> (i * i).toString() })

Returns and Jumps

跳转标签@

loop@ for (i in 1..100) { for (j in 1..100) { if (...) break@loop } }

Class类

class MyClass {

}

如果没有body的话,则都不需要大括号 class MyClass

构造器

主要的构造器

class Person constructor(firstName: String) { }

如果没有注解,或者可见性描述,constructor可以被忽略:

class Person(firstName: String) { }

构造器无法包含初始化代码,解决方案init

class Person(firstName: String) { init { firstName = "pan" } }

次要构造器

关键字constructor

class Person { constructor(firstName: String) { firstName = "pan2" } }

构造器重载时“继承”其他构造器,如下:

class Person(val firstName: String) { constructor(firstName: String, subName: String) : this(name) { subName = "XXX" } }

class类里可以包含

  • Constructors and initializer blocks
  • Functions
  • Properties
  • Nested and Inner Classes
  • Object Declarations

类继承open

open表示可以被继承,跟java中的final意义相反

open class Base(p: Int) class Derived(p: Int) : Base(p)

如果没有主要构造器,则使用super关键字实例化父类 class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) }

方法重写

方法必须显示的标记为open,子类override【方法标记为open的前提是类是open的】

open class Base { open fun v() {} fun nv() {} } class Derived() : Base() { override fun v() {} }

子类可以中弄这种继承,只要加上final open class AnotherDerived() : Base() { final override fun v() {} }

属性重写

跟方法重写类似,采用open关键字,或者abstract都是可以的

open class Foo { open val x: Int get { ... } }

class Bar1 : Foo() { override val x: Int = ... }

多继承

open class A { open fun f() { print("A") } fun a() { print("a") } }

// 接口默认是open形式 interface B { fun f() { print("B") } // interface members are 'open' by default fun b() { print("b") } }

class C() : A(), B { // The compiler requires f() to be overridden: override fun f() { super.f() // call to A.f() super.f() // call to B.f() } }

抽象类

抽象类也是默认open开启的,抽象方法可以不需要方法体。

open class Base { open fun f() {} }

abstract class Derived : Base() { override abstract fun f() }

Companion Object

静态方法

class MyClass { companion object Factory { fun create(): MyClass = MyClass() } }

val instance = MyClass.create() val instance = MyClass.Factory.create() // 都可以

上面companion object的名称可以不要 class MyClass { companion object { } }

val x = MyClass.Companion

属性的getter和setter

var name: String =name get() = field.toUpperCase() set(value) { field = value }

如果只是改变可见性

var setterVisibility: String = "abc" private set // the setter is private and has the default implementation

编译时常量const

编译时常量需要的条件

  • Top-level or member of an object
  • Initialized with a value of type String or a primitive type
  • No custom getter

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

不知道啥作用~~

延时初始化属性lateinit

Kotlin给我们提供了声明一个非空变量而不需要设定初始值的功能,使用lateinit即可,变量将在他被使用的时候设定初始值

只能用在var属性,并且属性没有定制的getter或setter,

接口和抽象类

  • 接口不能保存状态,可以有属性但必须是抽象的,而抽类型可以有属性。
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

kotlin的接口里可以有:属性、方法

属性是不能被初始化,方法可以有body,也可以没有

可见性

  • private:本类可见
  • protected:本类 + 子类可见
  • public(default)
  • internal:同一个模块内可见

package foo

private fun foo() {} // visible inside example.kt

public var bar: Int = 5 // property is visible everywhere private set // setter is visible only in example.kt

internal val baz = 6 // visible inside the same module

修改主构造器的可见性

前提条件:必须显示的使用constructor关键字

class C private constructor(a: Int) { ... }

扩展

对某个类在不继承的情况下,实现方法或属性的扩展。

实现的方式很简答,确定一个接收的类,后面带上自己的方法名。

fun String.spaceToCamelCase() { ... } "Convert this to camelcase".spaceToCamelCase()

可为空的接收者

fun Any?.toNullString(): String { if (this == null) return "null" return toString() }

属性的扩展

val String.myChar: Char get() { return this[0] }

静态方法

class MyClass { companion object { } // will be called "Companion" }

fun MyClass.Companion.foo() { // ... }

// 使用 MyClass.foo()

Sealed Classes

sealed class Expr data class Const(val number: Double) : Expr() data class Sum(val e1: Expr, val e2: Expr) : Expr() object NotANumber : Expr()

Sealed Class的唯一用处好像是在用when的时候,例如:

fun eval(expr: Expr): Double = when(expr) { is Const -> expr.number is Sum -> eval(expr.e1) + eval(expr.e2) NotANumber -> Double.NaN // the else clause is not required because we've covered all the cases }

泛型

class Box(t: T) { var value = t }

val box: Box = Box(1)

嵌套类

class Outer { private val bar: Int = 1 class Nested { fun foo() = 2 } }

val demo = Outer.Nested().foo() // == 2

内部类

class Outer { private val bar: Int = 1 inner class Inner { fun foo() = bar } }

val demo = Outer().Inner().foo() // == 1

匿名内部类

window.addMouseListener(object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { // ... }

override fun mouseEntered(e: MouseEvent) {
    // ...
}

})

对象Object

对象表达式

open class A(x: Int) { open val y: Int = x }

interface B { fun doit() }

val ab: A = object : A(1), B { override fun doit() {

}

override val y = 15

}

我们 “只需要对象”, 而不需要继承任何有价值的基类, 我们可以简单地写: val adHoc = object { var x: Int = 0 var y: Int = 0 } print(adHoc.x + adHoc.y)

对象声明

通过Object声明单例模式

object DataProviderManager { fun registerDataProvider(provider: DataProvider) { // ... }

val allDataProviders: Collection<DataProvider>
    get() = // ...

}

DataProviderManager.registerDataProvider(...)

也可以指定基类 object DefaultListener : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { // ... }

override fun mouseEntered(e: MouseEvent) {
    // ...
}

}

对象表达式与对象声明在语义上存在一个重要的区别:

  • 对象表达式则会在使用处 立即 执行(并且初始化)
  • 对象声明是 延迟(lazily) 初始化的, 只会在首次访问时才会初始化 * 同伴对象会在对应的类被装载(解析)时初始化, 语义上等价于 Java 的静态初始化代码块(static initializer)

枚举类

enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF) }

委托

委托主要是通过by关键字

委托可以认为是继承的的一种很好的替代方案,而委托的类也可以重写被委托里的方法,改变他的行为。

interface Base { fun print() }

class BaseImpl(val x: Int) : Base { override fun print() { print(x) } }

class Derived(b: Base) : Base by b

fun main(args: Array) { val b = BaseImpl(10) Derived(b).print() // 输出 10 }

属性委托

class Example { var p: String by Delegate() }

class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" }

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value has been assigned to '${property.name} in $thisRef.'") } }

fun main(args: Array) { val e = Example() println(e.p) }

  • getValue()第一个参数是读出p的对象,第二个参数保存了对p自身的描述;
  • setValue()前2个参数相同,第三个参数就是将要被赋的值

延迟属性 Lazy

lazy() 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lamda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

val lazyValue: String by lazy { println("computed!") "Hello" }

fun main(args: Array) { println(lazyValue) println(lazyValue) }

可观察属性 Observable

Delegates.observable() 接受两个参数:初始值和修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值和新值:

class User { var name: String by Delegates.observable("") { prop, old, new -> println("$old -> $new") } }

fun main(args: Array) { val user = User() user.name = "first" user.name = "second" }

属性赋值前的拦截 vetoable

在属性被赋新值生效之前会调用传递给 vetoable 的处理程序。返回布尔值,用以表示这次设置是否有效。

class User { var s: Int by Delegates.vetoable(0) { d, old, new -> println("$old -> $new") new < 20 } }

fun main(args: Array) { val user = User() user.s = 30

print(user.s)

}

把属性储存在映射中

一个常见的用例是在一个映射(map)里存储属性的值。这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。

class User(val map: Map) { val name: String by map val age: Int by map }

val user = User(mapOf( "name" to "John Doe", "age" to 25 ))

fun main(args: Array) { println(user.name) }

函数的总结

中缀表示符infix

中缀表示符的使用有如下先决条件:

  • 是成员函数或者扩展函数
  • 只能有1个参数
  • 使用infix关键字标注

infix fun Int.shl2(x: Int): Int { return x * 2 }

fun main(args: Array) { 1 shl2 2 1.shl2(2) }

单表达式函数

当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可。

fun double(x: Int): Int = x * 2

可变数量的参数(Varargs)

函数的参数(通常是最后一个)可以用 vararg 修饰符标记:

fun asList(vararg ts: T): List { val result = ArrayList() for (t in ts) // ts is an Array result.add(t) return result }

当我们调用 vararg-函数时,我们可以一个接一个地传参,例如 asList(1, 2, 3),或者,如果我们已经有一个数组并希望将其内容传给该函数,我们使用伸展(spread)操作符(在数组前面加 *):

val a = arrayOf(1, 2, 3) val list = asList(-1, 0, *a, 4)

局部函数

就是函数内部可以嵌入另一个函数。并且内部函数可以调用外层函数的变量。

fun dfs(graph: Graph) { val visited = HashSet() fun dfs(current: Vertex) { if (!visited.add(current)) return for (v in current.neighbors) dfs(v) }

dfs(graph.vertices[0])

}

尾递归函数

tailrec fun findFixPoint(x: Double = 1.0): Double = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

fun main(args: Array) { print(findFixPoint()) }

好像不用tailrec也一样。。。。

Lambda

高阶函数

高阶函数是将函数作为参数,或者返回值的函数。

fun lock(lock: Lock, body: () -> T): T { lock.lock() try { return body() } finally { lock.unlock() } }

fun main(args: Array) { fun body() { sharedResource.operation() }

lock(ReentrantLock(), ::body)

}

Lambda表达式

Lambda表达式是对高阶函数的进一步优化,它的特点是:

  • 表达式总是被大括号括着
  • 其参数(如果有的话)在 -> 之前声明(参数类型可以省略)
  • 函数体(如果存在的话)在 -> 后面

采用lambda方式,则写法:

lock(lock, { sharedResource.operation() })

匿名函数

fun(x: Int, y: Int): Int { return x + y }

闭包

好像跟lambda差不多

内联函数

高阶函数的运行效率不高,每个函数都是一个对象,内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。

而内联函数可以消除这类的开销。

使用inline关键字即可: inline fun lock(lock: Lock, body: () -> T): T { // …… }

内联函数其实是将高阶函数扩展成了实际展开的样子,从而减少了运行时的开销。

如果只想是其中的某些参数被内联,而其他的参数还是走原来的流程,可以使用noinline修饰符进行标记:

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { // …… }

results matching ""

    No results matching ""