定义方法的方式
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
val box: Box
嵌套类
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
属性委托
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
- getValue()第一个参数是读出p的对象,第二个参数保存了对p自身的描述;
- setValue()前2个参数相同,第三个参数就是将要被赋的值
延迟属性 Lazy
lazy() 是接受一个 lambda 并返回一个 Lazy
val lazyValue: String by lazy { println("computed!") "Hello" }
fun main(args: Array
可观察属性 Observable
Delegates.observable() 接受两个参数:初始值和修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值和新值:
class User {
var name: String by Delegates.observable("
fun main(args: Array
属性赋值前的拦截 vetoable
在属性被赋新值生效之前会调用传递给 vetoable 的处理程序。返回布尔值,用以表示这次设置是否有效。
class User { var s: Int by Delegates.vetoable(0) { d, old, new -> println("$old -> $new") new < 20 } }
fun main(args: Array
print(user.s)
}
把属性储存在映射中
一个常见的用例是在一个映射(map)里存储属性的值。这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。
class User(val map: Map
val user = User(mapOf( "name" to "John Doe", "age" to 25 ))
fun main(args: Array
函数的总结
中缀表示符infix
中缀表示符的使用有如下先决条件:
- 是成员函数或者扩展函数
- 只能有1个参数
- 使用infix关键字标注
infix fun Int.shl2(x: Int): Int { return x * 2 }
fun main(args: Array
单表达式函数
当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可。
fun double(x: Int): Int = x * 2
可变数量的参数(Varargs)
函数的参数(通常是最后一个)可以用 vararg 修饰符标记:
fun
当我们调用 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
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
好像不用tailrec也一样。。。。
Lambda
高阶函数
高阶函数是将函数作为参数,或者返回值的函数。
fun
fun main(args: Array
lock(ReentrantLock(), ::body)
}
Lambda表达式
Lambda表达式是对高阶函数的进一步优化,它的特点是:
- 表达式总是被大括号括着
- 其参数(如果有的话)在 -> 之前声明(参数类型可以省略)
- 函数体(如果存在的话)在 -> 后面
采用lambda方式,则写法:
lock(lock, { sharedResource.operation() })
匿名函数
fun(x: Int, y: Int): Int { return x + y }
闭包
好像跟lambda差不多
内联函数
高阶函数的运行效率不高,每个函数都是一个对象,内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。
而内联函数可以消除这类的开销。
使用inline关键字即可:
inline fun lock
内联函数其实是将高阶函数扩展成了实际展开的样子,从而减少了运行时的开销。
如果只想是其中的某些参数被内联,而其他的参数还是走原来的流程,可以使用noinline修饰符进行标记:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { // …… }