第一章 数据类型
1.基础数据类型
Kotlin的基本数值类型包括Byte、Short、Int、Long、Float、Double等。不同于Java的是,字符不属于数值类型。
每个数据类型都有如下转换函数
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
字面值常量
主要是以下几种字面值常量:
--十进制数值: 123
--长整型要加大写 L : 123L
--16进制:0x0f
--二进制:0b00001011
注意不支持8进制
Kotlin 也支持传统的浮点数表示:
-- 默认双精度浮点数(Double) : 123.5 , 123.5e10
-- 单精度浮点数(Float)要添加 f 或 F :123.5f
数值常量中可以添加下划线分割(1.1版本新特性)
您可以使用下划线增加数值常量的可读性:
-- val oneMillion = 1_000_000
-- val creditCardNumber = 1234_5678_9012_3456L
-- val socialSecurityNumber = 999_99_9999L
-- val hexBytes = 0xFF_EC_DE_5E
-- val bytes = 0b11010010_01101001_10010100_10010010
值相等与物理值相比较
val a: Int = 10000
print (a === a ) // 打印 'true' ---注意区别是在这个三元等号
val boxedA: Int? =a
val anotherBoxedA: Int? = a
print (boxedA === anotherBoxedA ) // 注意这里打印的是 'false'
另一方面,它们是值相等的:
val a: Int = 10000
print(a == a) // 打印 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // 打印 'true'
2.变量和常量
1).变量
//定义一个字符变量,自动推导为字符串
var name ="张三丰" //也可以这样写 var name:String = "张三丰"
//或者
var addr:String="深圳"
//或者
var addr:String?="深圳"
2).常量
//定义一个常量
val company ="小牛在线"
//定义一个可以为空的量
val friend:String="张三"
3.字符串
1).字会串拼接
var name = "XiaoMin"
var age = 23
var xiaomin = "小明的名字是$name,年纪是$age"
println(xiaomin)
2).复杂字符拼接
//定义一个类
class Animal{
var name:String="Animal"
var age:Int = 10
}
//主函数
fun main(args: Array<String>) {
var animal = Animal()
println("animal name is ${animal.name}") //拼接字符
}
3).多行字符串
val text = """
Tell me and I forget
Teach me and I remember
Involve me and I learn
(Benjamin Franklin)
"""
//如果要去掉空字符,可以这样. 类似java的trim()
var text =""" Tell me and I forget """.trim-margin()
4.Any数据类型
Any是Kotlin所有数据的基类,类似于Java的Object. 但是它没有hashCode,toString等方法, 这是和java的区别.
var pig:Any = Animal("pig")
var dog:Any = Animal("dog")
class Animal(name:String) {}
5.集合数据类型
1). List集合的用法
var list = listOf<Pig>(
Pig("大猪", 10, "F"),
Pig("黑猪", 20, "M"),
Pig("白猪", 30, "F"),
Pig("小猪", 40, "M")
)
var list2 = listOf<Int>(
100, 1, 2, 3, 4, 5
)
//直接设用java的接口对集合排序
Collections.sort(list2)
//调用Kotlin接口排序
list.sortedBy({
it.age
})
//输出集合中的数据
for ((i, item) in list2.withIndex()) {
println("$i is $item")
}
for ((i, item) in list.withIndex()) {
println("${item.name} is ${item.age}")
}
2). Set集合的用法
//显示定义一个无序集合
var addr:Set<String> = setOf("深圳","上海")
//输出集合数据内容
for(item in addr.iterator()){
println(item)
}
//定义有序的Set
var friends:SortedSet<Int> = sortedSetOf(10,5,30,20,40)
//输出集合数据内容
for(item in friends.iterator()){
println(item)
}
3).Map集合的用法
//显示定义一个集合
var addr:Map<Int,String> = mapOf<Int,String>(1 to "深圳", 2 to "上海")
//或者
var friends = mapOf<Int,String>(1 to "小平",2 to "毛泽东")
//输出集合内容
for( (K,V) in addr.entries){
println("集合数据Key=$K,Value=$V")
}
for( K in addr.keys ){
println("集合数据Key=$K,Value=${addr.get(K)}")
}
//定义有序的map
var sortMap:SortedMap<Int,String> = sortedMapOf(2 to "深圳", 3 to "上海",1 to "南京")
for( K in sortMap.keys ){
println("有序Map Key=$K,Value=${addr.get(K)}")
}
6.数组
数组的用法简单好用,如下例子
fun main(args:Array<String>){
var intArray = intArrayOf(1,2,3,4,5)
var longArray = longArrayOf(1L,2L,3L,4L,5L)
var floatArray = floatArrayOf(10f,20f,30f,40f,50f)
var doubleArray = doubleArrayOf(10.1,10.2,20.3,30.3,40.4e10)
var byteArray = byteArrayOf(1,2,3,4)
var shortArray = shortArrayOf(1,23,3,4)
var animals:Array<Animal> = arrayOf(Animal("狗"), Animal("猪"))
}
class Animal(name:String){
var name:String
init {
this.name = name
}
}
第二章 操作符
1.判空操作符
1.判空操作符 ?
var name:String?="小平同志" //表示该变量可以为空
2.如果为空 ?:
var name:String?="小平同志" //表示该变量可以为空
var rename = name?:"习近平同志" //如果name不为null就把值给rename,否则rename就重新赋值
3.如果不为空才做一件事 ?.
var addr:String?="深圳"
//addr不为空才执行let中的任务
addr?.let {
println("to do something")
}
4.函数返值可以为空
fun request():Response?{
return null
}
class Response
2.等号操作符
相等,在 kotlin 中有两种相等:
1).===表示: 参照相等(指向相同的对象):内存单元相同
2).== 表示: 结构相等
1).参照相等
参照相等是通过 === 操作符判断的(不等是!== ) a===b 只有 a b 指向同一个对象是判别才成立。
另外,你可以使用内联函数 identityEquals() 判断参照相等:
a.identityEquals(b)
a identityEquals b
2).结构相等
结构相等是通过 == 判断的。像 a == b 将会翻译成:
a?.equals(b) ?: b === null
如果 a 不是 null 则调用 equals(Any?) 函数,否则检查 b 是否参照等于 null
注意完全没有必要为优化你的代码而将 a == null 写成 a === null 编译器会自动帮你做的。
注意: 上面两种等号,字面意思有所不同,但实际效果没有区别.
var x1 ="a"
var x2 ="a"
println(x1 == x2)
println(x1 === x2)
println(x1.equals(x2))
//结果都是输出 true
3. in 和downTo
for (i in 20 downTo 15)
{
println(i)
}
for (i in 1..10) {
println(i)
}
第三章 控制流
1.IF表达式
在Kotlin中, 没有三元表达式,传统用法:
var max = a
if (a < b)
max = b
// With else
var max: Int
if (a > b)
max = a
else
max = b
IF 作为表达式的用法:
var a:Int = 1
var b:Int = 2
val max:Int = if (a > b) a else b
val min = if (a > b) a else b
IF作为返回值的用法:
fun asRetunExpression1(a:Int,b:Int):Int = if (a > b) a else b
fun asRetunExpression2(a:Int,b:Int):Int {
return if (a > b) a else b
}
fun asRetunExpression3(a:Int,b:Int):Int {
if (a > b) return a else return b
}
2.When表达式
替代Switch的用法
var x = 1
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
常规的When用法
var x ="2"
var i = 2
when (i) {
parseInt(x) -> print("s encodes x")
else -> print("s does not encode x")
}
我们可以在判断分支条件的地方使用任何表达式,而不仅仅是常量(和switch不同):
var x = 3
var validNumbers = 100..200
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
in , !in在When中的使用
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
有参数When,可以用 is来检测类型
val hasPrefix = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
无参数When的使用
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
查看 grammar for when{: .keyword }.
3.For循环
For循环主要有以下用法
//输出一个范围内的值
fun forExample1() {
for (i in 1..10) {
println(i)
}
}
//从20输出到位15
fun forExample2() {
for (i in 20 downTo 15) {
println(i)
}
}
//输出集全元素
fun forExample3() {
val arrayInts = arrayListOf("one", "two", "three")
for ((i, item) in arrayInts.withIndex()) {
println("index = $i, item value is = $item")
}
}
//in关键字在表达式中的用法
fun forExample4() {
var char = 'A'
var result = char in 'A'..'Z' || char in 'a'..'z'
println(result)
}
4.While循环
while{: .keyword } 和 do{: .keyword }..while{: .keyword } 的使用方法和其他语言一致
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y is visible here!
第四章 类和继承
1.类
类声明Kotlin使用关键字class
class Invoice {}
或者
clas Invoice
这个类声明被花括号包围,包括类名、类头(指定其类型参数,主构造函数等)和这个类的主干。类头和主干都是可选的; 如果这个类没有主干,花括号可以被省略。
class Empty
2.构造
在Kotlin中的类可以有主构造函数和一个或多个二级构造函数。主构造函数是类头的一部分:它跟在这个类名后面(和可选的类型参数)
class Person constructor(firstName: String) {
}
如果这个主构造函数没有任何注解或者可见的修饰符,这个constructor{: .keyword }关键字可以被省略
class Person(firstName: String) {
}
这个主构造函数不能包含任何的代码。初始化的代码可以被放置在initializer blocks(初始的模块),以init为前缀作为关键字{:.keyword}
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
请注意,主构造的参数可以在初始化模块中使用。它们也可以在类体内声明初始化的属性:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
事实上,声明属性和初始化主构造函数,Kotlin有简洁的语法:
class Person(val firstName: String, val lastName: String, var age: Int) {
// ...
}
与普通属性一样,主构造函数中声明的属性可以是可变的或者是只读的
If the constructor has annotations or visibility modifiers, the constructor{: .keyword } keyword is required, and the modifiers go before it: 如果构造函数有注解或可见性修饰符,这个constructor{: .keyword }需要被关键字修饰。
class Customer public inject constructor(name: String) { ... }
3.扩展构造函数
类也可以拥有被称为”二级构造函数”(为了实现Kotlin向Java一样拥有多个构造函数),通常被加上前缀”constructor”
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
如果类有一个主构造函数,每个二级构造函数需要委托给主构造函数,直接或间接地通过另一个二级函数。 委托到另一个使用同一个类的构造函数用this关键字
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一个非抽象类没有声明任何构造函数(原发性或继发性),这将有一个生成的主构造函数不带参数。构造函数的可见性是public。
如果你不希望你的类有一个公共构造函数,你需要声明与非缺省可见一个空的主构造函数:
class DontCreateMe private constructor () {
}
注意在JVM上,如果所有的主构造函数的参数有默认值,编译器会产生一个额外的参数的构造函数,将使用默认值。
class Customer(val customerName: String = "")
4.创建类的实例
要创建一个类的实例,我们调用构造函数,就好像它是普通的函数:
val invoice = Invoice()
val customer = Customer("Joe Smith")
注意Kotlin不能有“new”关键字
5.类成员
类可以包括
- 构造和初始化模块
- 函数
- 属性
- 匿名和内部类
- 对象声明
- 继承
- 伴随对象
在Kotlin所有的类中都有一个共同的父类Any,这是一个默认的父类且没有父类型声明:
class Example // Implicitly inherits from Any
Any不属于java.lang.Object;特别是,它并没有任何其他任何成员,甚至连equals(),hashCode()和toString()都没有。
要声明一个明确的父类,我们把类型放到类头冒号之后:
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标注可以理解为Java中final的反面,它允许其他他类 从这个类中继承。
默认情况下,在Kotlin所有的类都是final,.
对应于 Effective Java 书中的17条:设计并显示标注继承,否则就禁止它。
伴随对象实现单例,静态变量的效果
class Student(name: String) {
var name: String? = null
init {
this.name = name
}
companion object {
@Volatile private var student: Student?= null
fun getSingleton():Student? {
if (this.student == null) {
synchronized(this) {
this.student = Student("文忠湖")
}
}
return this.student
}
}
fun walk(){
println("walk")
}
}
fun main(args:Array<String>){
var student:Student? = Student.getSingleton()
student?.walk()
}
6.覆盖成员
我们之前提到过,Kotlin力求清晰显式。不像Java中,Kotlin需要明确的 标注覆盖的成员(我们称之为open)和重写的函数。(继承父类并覆盖父类函数时,Kotlin要求父类必须有open标注,被覆盖的函数必须有open标注,并且子类的函数必须加override标注。):
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
Derived.v()函数上必须加上override标注。如果没写,编译器将会报错。 如果父类的这个函数没有标注open,则子类中不允许定义同名函数,不论加不加override。 在一个final类中(即没有声明open的类),函数上也不允许加open标注。
成员标记为override 的本身是开放的,也就是说,它可以在子类中重写。如果你想禁止重写的,使用final 关键字:
open class AnotherDerived() : Base() {
final override fun v() {}
}
在Kotlin中,实现继承的调用通过以下规则:
如果一个类继承父类成员的多种实现方法,可以直接在子类中引用, 它必须重写这个成员,并提供其自己的实现(当然也可以使用父类的)。
为了表示从中继承的实现而采取的父类型,我们使用super 在尖括号,如规范的父名super:
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
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<A>.f() // call to A.f()
super<B>.f() // call to B.f()
}
}
类C同时继承A和B是可以的,而且我们在调用a()和b()函数时没有任何问题,因为他们在C的基类中只有一个实现。 但是f()函数则在A,B中都有实现,所以我们必须在C中覆盖f(),并且提供我们的实现以消除歧义。
抽象类
类和其中的某些实现可以声明为abstract{:.keyword}。 抽象成员在本类中可以不用实现。。 因此,当一些子类继承一个抽象的成员,它并不算是一个实现:
abstract class A {
abstract fun f()
}
interface B {
open fun f() { print("B") }
}
class C() : A(), B {
// We are not required to override f()
}
需要注意的是,我们并不需要标注一个抽象类或者函数为open
我们可以重写一个open非抽象成员使之为抽象的。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
第五章 属性和字段
1.声明属性
Kotlin的属性. 这些声明是可变的,用关键字var 或者使用只读关键字val .
public class Address {
public var name: String = ...
public var street: String = ...
public var city: String = ...
public var state: String? = ...
public var zip: String = ...
}
要使用一个属性,只需要使用名称引用即可,就相当于Java中的公共字段:
fun copyAddress(address: Address): Address {
val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
2.延迟属性
Kotlin声明一个属性一定要初始化, 当你不想初始化的时候就可以用lazy关键字来处理了
val lazyValue: String by lazy {
println("computed!") //第一次初始化,会输出这一行内容
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
3.getters和setters
声明一个属性的完整语法
var <propertyName>: <PropertyType> [= <property_initializer>]
<getter>
<setter>
上面的定义中,初始器(initializer)、getter和setter都是可选的。 属性类型(PropertyType)如果可以从初始器或者父类中推导出来,也可以省略。
例如:
var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter
注意公有的API(即public和protected)的属性,类型是不做推导的。 这么设计是为了防止改变初始化器时不小心改变了公有API。比如:
public val example = 1 // error: a public property must have a type specified explicitly
一个只读属性的语法和一个可变的语法有两方面的不同:
- 1·只读属性的用val开始代替var
- 2·只读属性不许setter
val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter
我们可以编写自定义的访问器,非常像普通函数,对内部属性声明。这里有一个定义的getter的例子:
val isEmpty: Boolean
get() = this.size == 0
一个定义setter的例子:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
按照惯例,setter参数的名称是“value”,但是如果你喜欢你可以选择一个不同的名称。
如果你需要改变一个访问器或注释的可见性,但是不需要改变默认的实现, 您可以定义访问器而不定义它的实例:
var setterVisibility: String = "abc" // Initializer required, not a nullable type
private set // the setter is private and has the default implementation
var setterWithAnnotation: Any?
@Inject set // annotate the setter with Inject
第六章 接口
Kotlin 的接口很像 java 8。它们都可以包含抽象方法,以及方法的实现。和抽象类不同的是,接口不能保存状态。可以有属性但必须是抽象的。
接口是通过关键字 interface 来定义的:
interface MyInterface {
fun bar()
fun foo() {
//函数体是可选的-和java8类似
}
}
1.接口的实现
一个类或对象可以实现一个或多个接口
class Child : MyInterface {
fun bar () {
//函数体
}
}
interface MyInterface {
val property: Int //抽象属性
fun foo() {
print(property)
}
}
class Child : MyInterface {
override val property: Int = 29
}
2.抽象类
用关键字abstract 定义抽象类 ,抽象方法也需要用abstract .
interface Biology{
fun walk()
fun eat()
}
abstract class Animal(name: String, age: Int):Biology {
open fun sleep(){
println("sleep()")
}
abstract fun laugh()
}
3.继承
注意构造方法的传参,Kotlin也只支持单继承.
class Pig(name:String,age:Int,sex:String): Animal(name,age,sex){
override fun laugh() {
println(" $name 都笑了.")
}
override fun walk() {
println("$sex $name $age years old : walk. ")
}
override fun eat() {
println("$sex $name $age years old : eat.")
}
override fun sleep() {
println("$sex $name $age years old : sleep.")
}
}
4.解决冲突
当我们在父类中声明了许多类型,有可能出现一个方法的多种实现。比如:
interdace A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}
A B 接口都有声明了 foo() bar() 函数。它们都实现了 foo() 方法,但只有 B 实现了 bar() ,bar() 在 A 中并没有声明它是抽象的,这是因为在接口中如果函数没有函数体,那么默认是抽像的。
现在,如果我们从 A 中派生一个 C 实体类,显然我们需要重写 bar() ,并实现它。而我们从 A 和 B 派生一个 D ,我们不用重写 bar() 方法,因为我们的一个继承中有一个已经实现了它。
但我们继承了俩个 foo() 的实现,因此编译器不知道应该选哪个,并强制我们重写 foo() 并且明确指出我们想怎么实现。
第七章 函数与属性扩展
Kotlin 与 C# Gosu 类似,提供了不用从父类继承,或者使用像装饰模式这样的设计模式来给某个类进行扩展。这是通过叫扩展的特殊声明来达到的。现在, Kotlin 支持扩展函数和属性。
1.扩展函数
声明一个扩展函数,我们需要添加一个接收者类型的的前缀。
下面是给MutableList添加一个 swap 函数的例子:
fun MutableList<Int>.swap(x: Int, y: Int) {
val temp = this[x] // this 对应 list
this[x] = this[y]
this[y] = tmp
}
在扩展函数中的 this 关键字对应接收者对象。现在我们可以在任何 MutableList 中使用这个函数了:
fun <T> MutableList<T>.swap(x: Int, y: Int) {
val tmp = this[x] // 'this' corresponds to the list
this[x] = this[y]
this[y] = tmp
}
2.可空的接受者
值得注意的是扩展可以定义一个可空的接受者。这样的扩展可以算作对象的变量,即使它是空的,你可以在函数体内检查 this == null 。这样你就可以在 Kotlin 中不进行空检查就可以调用 toString() 方法:这样的检查是在扩展函数中做的。
fun Any?.toString(): String {
if (this == null) return "null"
return toString()
}
3.扩展属性
和函数类似, Kotlin 也支持属性扩展:
val <T> List<T>.lastIndex: Int
get() = size-1
注意,扩展并不是真正给类添加了成员,没有比给扩展添加备用字段更有效的办法了/这就是为什么初始化函数是不允许扩展属性。它们只能通过明确提供 getter setter 来作用。
4.伴随对象扩展
如果一个对象定义了伴随对象,你也可以给伴随对象添加扩展函数或扩展属性:
class MyClass {
companion object {}
}
fun Myclass.Companion.foo() {
}
和普通伴随对象的成员一样,它们可以只用类的名字就调用:
MyClass.foo()
5.扩展的范围
大多数时候我们在 top level 定义扩展,就在包下面直接定义:
package foo.bar
fun Baz.goo() { ... }
在声明的包外面使用这样的扩展,我们需要在 import 时导入:
package com.example,usage
import foo.bar.goo//导入所有名字叫 "goo" 的扩展
import foo.bar.*
fun usage(baz: Baz) {
baz.goo()
}
6.可变参数Varargs
fun request(vararg params:String){
for( (i,item) in params.withIndex() ){
println("$i,$item")
}
}
fun main(args:Array<String>){
request("fruit","pig","plant")
}
第八章 数据类
我们经常创建一些只是处理数据的类。在这些类里的功能经常是衍生自他们所持有的数据。在Kotlin中,这样的类可以被称为data:
data class User(val name: String, val age: Int)
这被叫做一个 数据类。编译器自动从在主构造函数定义的全部特性中得到以下成员:
equals()/hashCode(),
toString() 格式是 "User(name=John, age=42)",
componentN() functions 对应按声明顺序出现的所有属性,
copy() 方法 .
如果有某个函数被明确地定义在类里或者被继承,编译器就不会生成这个函数。
NOTE如果一个构造参数没有val或者var在前面,它将不会被包括进这些成员里;也不会在类里声明成属性或者继承自父类
在JVM中,如果生成的类需要含有一个无参的构造函数,则所有的属性必须有默认值。
data class User(val name: String = "", val age: Int = 0)
1.复制
在很多情况下,我们我们需要对一些属性做修改而其他的不变。这就是copy()这个方法的来源。对于上文的User类,应该是这么实现这个方法的
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
也可以这么写
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
2.数据类和多重声明
成员方法用于使数据类可以多声明:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
3.标准数据类
在标准库提供了Pair和Triple。在很多情况下,即使命名数据类是一个更好的设计选择,因为这能让代码可读性更强。
第九章 内部类
1.静态内部类
不可以访外部类的属性
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val nested = Outer.Nested()
nested.foo()
2.非静态内部类
加上Inner关键字,就是非静态内部类. 内部类会带有一个来自外部类的对象的引用,且可以访问外部类的属性方法.
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val inner= Outer.Inner()
inner.foo()
第十章 安全构建
请参考官网:
第十一章 其它
1.异常
1). try作为表达式
fun tryException(){
var result = try{
Integer.parseInt("2"); //如果转换成功,结果会赋值给result
}catch (a: Exception){
null //如果转换失败,null会赋值给result
}finally{
println("not as a result") // 这里不会作为结果赋值给result
}
println(result)
}
2).自定义异常
类似于java一样的写法, 实际上它就是调用的java的异常基类
class ExceptionExtend(message:String):Exception(message){
}
我们可以查看Kotlin源码,他作了一个映射, Kotlin映射java里面的类
@SinceKotlin("1.1") public typealias Error = java.lang.Error
@SinceKotlin("1.1") public typealias Exception = java.lang.Exception
2.反射
直接看例子, 网上所有教程都只对 ::简单说明了一下,在这里给出例子更容易懂.
fun main(args:Array<String>){
var tmp = Teacher()
println(tmp::class)
println(tmp::class.simpleName)
println(tmp::class.qualifiedName)
var members = tmp::class.members
for( (i,item) in members.withIndex()){
if(item.name == "walk")
item.call(tmp) //类似java的invoke方法.
}
}
class Student{
var name:String?="student"
var age:Int=1
fun walk(){
println("walk")
}
}
3.对象解构
下面代码不能正常运行,暂时找不到足够资料解,先当作了解.
var teacher = Teacher("习近平",50)
var (name:String?,age:Int) = teacher
4.this表达式
这是官网给出的例子:
class A { // implicit label @A
inner class B { // implicit label @B
fun Int.foo() { // implicit label @foo
val a = this@A // A's this
val b = this@B // B's this
val c = this // foo()'s receiver, an Int
val c1 = this@foo // foo()'s receiver, an Int
val funLit = lambda@ fun String.() {
val d = this // funLit's receiver
}
val funLit2 = { s: String ->
// foo()'s receiver, since enclosing lambda expression
// doesn't have any receiver
val d1 = this
}
}
}
}
5.类的代理
使用by关键代理一个类, 它是替换继承的一个不错方案.
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 一定要是接口才可以被代理, 这里的Base是接口.
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
}
7.属性代理
有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们, 但是如果能够为大
家把他们只实现一次并放入一个库会更好。例如包括
- 延迟属性(lazy properties): 其值只在首次访问时计算,
- 可观察属性(observable properties): 监听器会收到有关此属性变更的通知,
- 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。
为了涵盖这些(以及其他)情况,Kotlin 支持 委托属性:
class Example {
var p: String by Delegate()
}
属性代理不必实现任何的接口,但是需要提供一个 getValue() 函数(和 setValue()
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.'")
}
}
当我们从委托到一个 Delegate 实例的 p 读取时,将调用 Delegate 中的 getValue() 函
数, 所以它第一个参数是读出 p 的对象、第二个参数保存了对 p 自身的描述.
可观察属性 Observable
Delegates.observable() 它有三个 参数:被赋值的属性、旧值和新值:
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<default>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
//这个例子输出:
<default> -> first
first -> second
如果你想能够截获一个赋值并“否决”它,就使用 vetoable() 取代 observable() 。 在属性被
赋新值生效之前会调用传递给 vetoable 的处理程序。
import kotlin.properties.Delegates
class User {
var name: String by Delegates.vetoable(""){
prop, old, new ->
if(new =="not allow value")
false
else
true
}
}
把属性储存在映射中
一个常见的用例是在一个映射(map)里存储属性的值。 这经常出现在像解析 JSON 或者做
其他“动态”事情的应用中。 在这种情况下,你可以使用映射实例自身作为委托来实现委托属
性。
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
在这个例子中,构造函数接受一个映射参数:
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
委托属性会从这个映射中取值(通过字符串键——属性的名称):
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
//这也适用于 var 属性,如果把只读的 Map 换成 MutableMap 的话:
class MutableUser(val map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
8.注解
annotation class Document
9.别名
1).当一个类型特别长的时候,就非常有用.
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
2).你也可以为函数起别名
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
3).也可以为内部类起别名
class A {
inner class Inner
}
class B {
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner