TIP : www.kotlincn.net/docs/refere… 官方文档
Byte
Short
Int
Long
Float
Double Char(注:char不是数值类型,是一个独立数据类型
在 Java 中 char 是 int 的一部分,可以直接隐式转换成 int,
在 Kotlin 中 Char 不再是 Int 的一部分了,而是一个单独的数据类型,不可以直接转换。)
int? 带有?符号是会被装箱操作 Integer.
定义常量与变量
变量就是用来存储数据,而函数就是用来处理数据。
var <标识符> : <类型> = <初始化值>
val <标识符> : <类型> = <初始化值>
val a: Int = 1
val b = 1 // 系统自动推断变量类型为Int
val c: Int // 如果不在声明时初始化则必须提供变量类型
c = 1 // 明确赋值
var x = 5 // 系统自动推断变量类型为Int
x += 1 // 变量可修改字符串模板
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值:
var a = 1
// 模板中的简单名称:
val s1 = "a is $a"
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"NULL检查机制
Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理 . 当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。
//类型后面加?表示可为空 会对变量做一次非空确认之后再调用方法
var age: String? = "23"
//不做处理返回 null 先做非空确认 再调用 线程安全做法
val ages1 = age?.toInt()
//抛出空指针异常 断言式 我保证这里的 view 一定是非空的,告诉编译器有问题我自己负责
val ages = age!!.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
lateinit 延迟初始化 以上是关于空的处理类型检测及自动类型转换
使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
//在这里还有一种方法,与Java中instanceof不同,使用!is
// if (obj !is String){
// // XXX
// }
// 这里的obj仍然是Any类型的引用
return null
}区间
区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
// 使用 until 函数排除结束元素
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}比较两个数字
三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。
fun main(args: Array<String>) {
val a: Int = 10000
println(a === a) // true,值相等,对象地址相等
//经过了装箱,创建了两个不同的对象
val boxedA: Int? = a
val anotherBoxedA: Int? = a
//虽然经过了装箱,但是值是相等的,都是10000
println(boxedA === anotherBoxedA) // false,值相等,对象地址不一样
println(boxedA == anotherBoxedA) // true,值相等
}For循环
for (item in collection) print(item)
for (item: Int in ints) {
// ……
}
for ((index, value) in array.withIndex()) {
// 下标及值
println("the element at $index is $value")
}
getter 和 setter
v不允许设置setter函数,因为它是只读的 lateinit 提供一种可以延迟初始化
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter
val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter
实例:tip field 关键词只能用于属性的访问器,
class Person { var lastName: String = "zhang"
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
var heiht: Float = 145.4f
private set
}
field:
定义 var no: Int 变量,当你写出 no = ... 这种形式的时候,这个等于号都会被编译器翻译成调用 setter 方法;
而同样,在任何位置引用变量时,只要出现 no 变量的地方都会被编译器翻译成 getter 方法。那么问题就来了,
当你在 setter 方法内部写出 no = ... 时,相当于在 setter 方法中调用 setter 方法,形成递归,进而形成死循环
类的属性
1 val site = Runoob() // Kotlin 中没有 new 关键字
2 class Person constructor(firstName: String) {
// 可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后
}
3 class Person {
//次构造函数
constructor(parent: Person) {
parent.children.add(this) }
}
4 class DontCreateMe private constructor () {
//私有的
}
5 class Runoob constructor(name: String) { // 类名为 Runoob
// 大括号内是类体构成 var url: String = "http://www.runoob.com"
var country: String = "CN"
var siteName = name
init {
println("初始化网站名: ${name}")
}
// 次构造函数
constructor (name: String, alexa: Int) : this(name) {
println("Alexa 排名 $alexa")
}
fun printTest() {
println("我是类的函数")
}
}
fun main(args: Array<String>) {
val runoob = Runoob("my is mrwang", 10000)
println(runoob.siteName)
println(runoob.url)
println(runoob.country)
runoob.printTest()
}
输出结果:
初始化网站名: my is mrwang
Alexa 排名 10000
my is mrwang
http://www.runoob.com
CN
我是类的函数内部类
内部类使用 inner 关键字来表示。
类的修饰符
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承,类默认是final的
annotation // 注解类
访问权限修饰符
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见
如果没有显式指定修饰符的话,默认可见性是 public
继承
- 使用 open 关键字进行修饰。 所有类都有一个共同的超类Any 默认超类
Any有三个方法:equals()、hashCode()与toString()- 子类有主构造函数
open class Person(var name : String, var age : Int){// 基类
}
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
}- 子类没有主构造函数
次类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类
class Student : Person {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}重写
使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词
/**用户基类**/
open class Person{
open fun study(){ // 允许子类重写
println("我毕业了")
}
}
/**子类继承 Person 类**/
class Student : Person() {
override fun study(){ // 重写方法
println("我在读大学")
}
}有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现
open class A {
open fun f () { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } //接口的成员变量默认是 open 的
fun b() { print("b") }
}
class C() : A() , B{
override fun f() {
super<A>.f()//调用 A.f()
super<B>.f()//调用 B.f()
}
}子类继承父类时,不能有跟父类同名的变量,除非父类中该变量为 private,或者父类中该变量为 open 并且子类用 override 关键字重写:
open class Person(var name: String, var age: Int) {
open var sex: String = "unknow"
init {
println("基类初始化")
}
}
// 子类的主构造方法的 name 前边也加了 var,这是不允许的,会报'name' hides member of supertype and needs 'override' modifier
class Student(var name: String, age: Int, var no: String, var score: Int) : Person(name, age) {
override var sex: String = "male"
}属性重写
属性重写使用 override 关键字
接口
interface MyInterface {
fun bar() // 未实现
fun foo() { //已实现
// 可选的方法体
println("foo")
}
}扩展函数
可以在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式:
class User(var name:String){
}
/**扩展函数**/
fun User.Print(){
this指向User作用域
print("用户名 $name")
}
!!!!! 真是些神奇的骚操作-_-
扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数
class C {
fun foo() { println("成员函数") }
}
fun C.foo() { println("扩展函数") }扩展函数是静态解析的
扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的:
open class C
class D: C()
fun C.foo() = "c" // 扩展函数 foo
fun D.foo() = "d" // 扩展函数 foo
fun printFoo(c: C) {
println(c.foo()) // 类型是 C 类
}
fun main(arg:Array<String>){
printFoo(D())
}
实例执行输出结果为: c扩展属性
扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。
初始化属性因为属性没有后端字段(backing field),所以不允许被初始化
只能由显式提供的 getter/setter 定义。
val <T> List<T>.lastIndex: Int
get() = size - 1
val Foo.bar = 1 // 错误:扩展属性不能有初始化器 只能被声明为 val。
扩展的作用域
扩展函数或属性定义在顶级包下:
package foo.bar
fun Baz.goo() { …… } 对象表达式
对象表达式实现一个匿名内部类的对象用于方法的参数中
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
对象可以继承于某个基类,或者实现其他接口
open class A(x: Int) {
public open val y: Int = x
}
interface B {……}
val ab: A = object : A(1), B {
override val y = 15
}
通过对象表达式可以越过类的定义直接得到一个对象
fun main(args: Array<String>) {
val site = object {
var name: String = "this name"
var url: String = "www.baidu.com"
}
println(site.name)
println(site.url)
}
对象声明
Kotlin 使用 object 关键字来声明一个对象。
Kotlin 中我们可以方便的通过对象声明来获得一个单例。
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ……
}
val allDataProviders: Collection<DataProvider>
get() = // ……
}
使用:DataProviderManager.registerDataProvider(……)对象表达式和对象声明之间的语义差异
对象表达式和对象声明之间有一个重要的语义差别:
对象表达式是在使用他们的地方立即执行的
对象声明是在第一次被访问到时延迟初始化的
伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配类委托
即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。
// 创建接口
interface Base {
fun print()
}
// 实现此接口的被委托的类
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 输出 10
}属性委托
属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。
val/var <属性名>: <类型> by <表达式>by 关键字之后的表达式就是委托, 属性的 get() 方法(以及set() 方法)将被委托给这个对象的 getValue() 和 setValue() 方法。属性委托不必实现任何接口, 但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)。
参数 thisRef 为进行委托的类的对象,property为进行委托的类的属性
import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, 这里委托了 ${property.name} 属性"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$thisRef 的 ${property.name} 属性赋值为 $value")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = "Runoob" // 调用 setValue() 函数
println(e.p)
}
输出:
Example@433c675d, 这里委托了 p 属性
Example@433c675d 的 p 属性赋值为 Runoob
Example@433c675d, 这里委托了 p 属性
标准委托
Kotlin 的标准库中已经内置了很多工厂方法来实现属性的委托。 ^蛋疼 好不容易看懂过程^
延迟属性 Lazy
lazy() 是一个函数, 接受一个 Lambda 表达式作为参数, 返回一个 Lazy <T> 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lamda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。(没搞懂它的作用 就没写例子了)
可观察属性 Observable
observable 可以用于实现观察者模式
Delegates.observable() 函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler)。
class User {
var name: String by Delegates.observable("初始值") {
prop, old, new ->
println("旧值:$old -> 新值:$new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "第一次赋值"
user.name = "第二次赋值"
}续……