本文主要讲解 Kotlin 基础语法。java
快速认识 Kotlin
基础语法
扩展函数
委托
结尾
Kotlin 是著名 IDE 公司 JetBrains 创造出的一门基于 JVM 的语言。Kotlin 有着一下几个特色:git
JetBrains 不只创造了 Kotlin,还创造了著名的 IntelliJ IDEA。Android 开发者使用的 Android Studio 就是基于 IntelliJ 改造出来的。github
与 Java 不同是:Kotlin 没有基本数据类型
(Primitive Types),全部 Kotlin 里面的类都是对象,他们都继承自: Any
这个类;与 Java 相似的是,Kotlin 提供了以下的内置类型:安全
Type | Bit width | 备注 |
---|---|---|
Double | 64 | Kotlin 没有 double |
Float | 32 | Kotlin 没有 float |
Long | 64 | Kotlin 没有 long |
Int | 32 | Kotlin 没有 int/Integer |
Short | 16 | Kotlin 没有 short |
Byte | 8 | Kotlin 没有 byte |
既然 Kotlin 与 Java 是兼容的,那么 Kotlin Int 与 Java int、Java Integer 之间是什么关系?bash
Kotlin Any 类型与 Java Object 类型之间有什么关系?多线程
修饰符 | 描述 |
---|---|
public | 与Java一致 |
private | 与Java一致 |
protected | 与Java一致 |
internal | 同 Module 内可见 |
var a: Int = 1
复制代码
val b: Int = 1
复制代码
val c = 1
复制代码
;
无关紧要:val d: Int;
d = 1;
复制代码
小结:框架
var
定义变量val
定义常量(~~不可变的变量、~~只读变量)Kotlin val 变量与 Java 的 final 有什么关系?ide
var b: String? = "Kotlin"
b = null
print(b)
// 输出 null
复制代码
var a: String = "Kotlin"
a = null
// 编译器报错,null 不能被赋给不为空的变量
复制代码
var a: String? = "Kotlin"
var b: String = "Kotlin"
b = a // 编译报错,String? 类型不能够赋值给 String 类型
a = b // 编译经过
复制代码
var a: String? = "Kotlin"
print(a.length) // 编译器报错,由于 a 是可为空的类型
print(a?.length) // 使用?. 的方式调用,输出 null
复制代码
// 下面两个语句等价
val l: Int = if (b != null) b.length else -1
val l = b?.length ?: -1
// Elvis 操做符在嵌套属性访问时颇有用
val name = userInstance?.user?.baseInfo?.profile?.name?: "Kotlin"
复制代码
小结:函数
if (x is String) {
print(x.length) // x 被编译自动转换为 String
}
// x is String 相似 Java 里的 instanceOf
复制代码
val y = null
val x: String = y as String
//抛异常,null 不能被转换成 String
复制代码
val y = null
val z: String? = y as? String
print(z)
// 输出 null
复制代码
小结:工具
is
关键字进行类型判断as
进行类型转换,可能会抛异常as?
进行安全的类型转换基础用法跟 Java 一毛同样。它们主要区别在于:Java If
is Statement,Kotlin If
is Expression。所以它对比 Java 多了些“高级”用法,懒得讲了。
跟 Java 也差很少,随便看代码吧:
// 集合遍历,跟 Java 差很少
for (item in collection) {
print(item)
}
// 辣鸡 Kotlin 语法
for (item in collection) print(item)
// 循环 1,2,3
for (i in 1..3) {
println(i)
}
// 6,4,2,0
for (i in 6 downTo 0 step 2) {
println(i)
}
复制代码
when 就至关于高级版的 switch,它的高级之处在于支持模式匹配(Pattern Matching)
:
val x = 9
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")
is String -> print("x is String")
x.isOdd() -> print("x is odd")
else -> print("none of the above")
}
// 输出:x is in the range
复制代码
Kotlin 有两种类型的相等性:
// 下面两句两个语句等价
a == b
a?.equals(b) ?: (b === null)
// 若是 a 不等于 null,则经过 equals 判断 a、b 的结构是否相等
// 若是 a 等于 null,则判断 b 是否是也等于 null
复制代码
print(a === b)
// 判断 a、b 是否是同一个对象
复制代码
val a: Int = 10000
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA)
print(boxedA === anotherBoxedA)
// 输出什么内容?
复制代码
val a: Int = 1
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA)
print(boxedA === anotherBoxedA)
// 输出什么内容?
复制代码
fun triple(x: Int): Int {
return 3 * x
}
// 函数名:triple
// 传入参数:不为空的 Int 类型变量
// 返回值:不为空的 Int 类型变量
复制代码
使用主构造器(Primary Constructor)定义类一个 Person 类,须要一个 String 类型的变量:
class Person constructor(firstName: String) { ... }
复制代码
若是主构造函数没有注解或者可见性修饰符,constructor 关键字可省略:
class Person(firstName: String) { ... }
复制代码
也可使用次构造函数(Secondary Constructor)定义类:
class Person {
constructor(name: String) { ... }
}
// 建立 person 对象
val instance = Person("Kotlin")
复制代码
Kotlin 为咱们提供了 init 代码块,用于放置初始化代码:
class Person {
var name = "Kotlin"
init {
name = "I am Kotlin."
println(name)
}
constructor(s: String) {
println(“Constructor”)
}
}
fun main(args: Array<String>) {
Person("Kotlin")
}
复制代码
以上代码输出结果为:
I am Kotlin.
Constructor
复制代码
结论:init 代码块执行时机在类构造以后,但又在“次构造器”执行以前。
类
,能够被继承方法
,能够被重写没有
open 关键字修饰的类,不可
被继承没有
open 关键字修饰的方法,不可
被重写Kotlin 的类和方法,默认状况下是 final 的
定义一个可被继承的
Base 类,其中的 add() 方法能够被重写
,test() 方法不可
被重写:
open class Base {
open fun add() { ... }
fun test() { ... }
}
复制代码
定义 Foo 继承 Base 类,重写 add() 方法
class Foo() : Base() {
override fun add() { ... }
}
复制代码
:
符号来表示继承override
重写方法class A {
fun testA(){ }
inner class B { // 在 class A 定义内部类 B
fun testB(){ }
fun foo() {
this.testB() // ok
this.testA() // 编译错误
this@A.testA() // ok
this@B.testB() // ok
}
}
}
复制代码
小结:
inner
关键字定义内部类this@OutterClass.fun()
的语法假设咱们有个这样一个 Java Bean:
public class Developer {
private String name;
public Developer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Developer developer = (Developer) o;
return name != null ? name.equals(developer.name) : developer.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
return result;
}
@Override
public String toString() {
return "Developer{" + "name='" + name + '}';
}
}
复制代码
若是咱们将其翻译成 Kotlin 代码,大约会是这样的:
class Developer(var name: String?) {
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o == null || javaClass != o.javaClass) return false
val developer = o as Developer?
return if (name != null) name == developer!!.name else developer!!.name == null
}
override fun hashCode(): Int {
return if (name != null) name!!.hashCode() else 0
}
override fun toString(): String {
return "Developer{" + "name='" + name + '}'.toString()
}
}
复制代码
然而,Kotlin 为咱们提供了另一种选择,它叫作数据类
:
data class Developer(var name: String)
复制代码
上面这一行简单的代码,彻底能替代
前面咱们的写的那一大堆模板 Java 代码,甚至额外多出了一些功能。若是将上面的数据类
翻译成等价的 Java 代码,大概会长这个样子:
public final class Developer {
@NotNull
private String name;
public Developer(@NotNull String name) {
super();
this.name = name;
}
@NotNull
public final String getName() { return this.name; }
public final void setName(@NotNull String var1) { this.name = var1; }
@NotNull
public final Developer copy(@NotNull String name) { return new Developer(name); }
public String toString() { return "Developer(name=" + this.name + ")"; }
public int hashCode() { return this.name != null ? this.name.hashCode() : 0; }
public boolean equals(Object var1) {
if (this != var1) {
if (var1 instanceof Developer) {
Developer var2 = (Developer)var1;
if (Intrinsics.areEqual(this.name, var2.name)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
复制代码
能够看到,Kotlin 的数据类不只为咱们提供了 getter、setter、equals、hashCode、toString,还额外的帮咱们实现了 copy 方法!这也体现了 Kotlin 的简洁
特性。
若是是旧工程迁移到 Kotlin,那么可能须要注意这个坑:
// 定义一个数据类,其中成员变量 name 是不可为空的 String 类型,默认值是 MOMO
data class Person(val age: Int, val name: String = "Kotlin")
val person = gson.fromJson("""{"age":42}""", Person::class.java)
print(person.name) // 输出 null
复制代码
对于上面的状况,因为 Gson 最初是为 Java 语言设计的序列化框架,并不支持 Kotlin 不可为空
、默认值
这些特性,从而致使本来不可为空的属性变成null
,本来应该有默认值的变量没有默认值。
对于这种情,市面上已经有了解决方案:
如何才能在不修改源码的状况下给一个类新增一个方法?好比我想给 Context 类增长一个 toast 类,怎么作?
若是使用 Java,上面的需求是没法被知足的。然而 Kotlin 为咱们提供了扩展
语法,让咱们能够轻松实现以上的需求。
为 Context 类定义一个 toast 方法:
fun Context.toast(msg: String, length: Int = Toast.LENGTH_SHORT){
Toast.makeText(this, msg, length).show()
}
复制代码
扩展函数的使用:
val activity: Context? = getActivity()
activity?.toast("Hello world!")
activity?.toast("Hello world!", Toast.LENGTH_LONG)
复制代码
除了扩展函数,Kotlin 还支持扩展属性
,用法基本一致。
上面的例子中,咱们给不可为空的
Context 类增长了扩展函数,所以咱们在使用这个方法的收须要判空。实际上,Kotlin 还支持咱们为 可为空的
类增长扩展函数:
// 为 Context? 添加扩展函数
fun Context?.toast(msg: String, length: Int = Toast.LENGTH_SHORT){
if (this == null) { //do something }
Toast.makeText(this, msg, length).show()
}
复制代码
扩展函数使用:
val activity: Context? = getActivity()
activity.toast("Hello world!")
activity.toast("Hello world!", Toast.LENGTH_LONG)
复制代码
请问这两种定义扩展函数的方式,哪一种更好?分别适用于什么情景?为何?
Kotlin 中,使用by
关键字表示委托:
interface Animal {
fun bark()
}
// 定义 Cat 类,实现 Animal 接口
class Cat : Animal {
override fun bark() {
println("喵喵")
}
}
// 将 Zoo 委托给它的参数 animal
class Zoo(animal: Animal) : Animal by animal
fun main(args: Array<String>) {
val cat = Cat()
Zoo(cat).bark()
}
// 输出结果:喵喵
复制代码
其实,从上面类委托的例子中,咱们就能知道,Kotlin 之因此提供委托这个语法,主要是为了方便咱们使用者,让咱们能够很方便的实现代理
这样的模式。这一点在 Kotlin 的委托属性
这一特性上体现得更是淋漓尽致。
Kotlin 为咱们提供的标准委托很是有用。
// 经过 by 关键字,将 lazyValue 属性委托给 lazy {} 里面的实现
val lazyValue: String by lazy {
val result = compute()
println("computed!")
result
}
// 模拟计算返回的变量
fun compute():String{
return "Hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println("=======")
println(lazyValue)
}
复制代码
以上代码输出的结果:
computed!
Hello
=======
Hello
复制代码
因而可知,by lazy 这种委托的方式,可让咱们轻松实现懒加载
。 其内部实现,大体是这样的:
Kotlin 为lazy 委托
提供三种线程模式,他们分别是:
上面这三种模式,前面两种很好理解:
咱们详细看看LazyThreadSafetyMode.PUBLICATION
,官方文档的解释是这样的:
Initializer function can be called several times on concurrent access to uninitialized [Lazy] instance value, but only the first returned value will be used as the value of [Lazy] instance.
意思就是,用LazyThreadSafetyMode.PUBLICATION
模式的 lazy 委托变量,它的初始化方法是可能会被多个线程执行屡次的,但最后这个变量的取值是仅以第一次算出的值为准的。即,哪一个线程最早算出这个值,就以这个值为准。
观察者模式
,又被称为订阅模式
。最多见的场景就是:好比读者们订阅了MOMO
公众号,每次MOMO
更新的时候,读者们就会收到推送。而观察者模式应用到变量层面,就延伸成了:若是这个的值改变了,就通知我
。
class User {
// 为 name 这个变量添加观察者,每次 name 改变的时候,都会执行括号内的代码
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("name 改变了:$old -> $new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "first: Tom"
user.name = "second: Jack"
}
复制代码
以上代码的输出为:
name 改变了:<no name> -> first: Tom
name 改变了:first: Tom -> second: Jack
复制代码
lazy 委托的LazyThreadSafetyMode.PUBLICATION
适用于什么样的场景?
看完了?别走!留下思考题答案吧!
继续阅读请点击-->【Kotlin Jetpack 实战】传送门