变量
在Kotlin语言中使用定义变量时,可以在变量名后用冒号跟上类型,明确指定变量类型,也可以利用编译器帮助类型推导,通过赋予变量值,由编译器自动匹配变量类型。
另外,Kotlin语言推荐在定义变量时使用val
关键字创建不可变类型,但并未做强制要求。因此在选择的情况下,如果后期不会对其值重复修改,就不用定义为var
了。
初始化
变量需要在使用前被初始化,具体的初始化实际不进行要求。
package introduction
fun main() {
var a: String = "initial" //声明了一个可变的变量并且对它进行初始化
println(a)
val b: Int = 1 //声明一个不可变的量并且初始化
val c = 3 //声明不可变的量,未指明类型,编译器推导类型为Int
// var e: Int
// println(e) 变量需要初始化
// Error:(11, 13) Kotlin: Variable 'e' must be initialized
val d: Int //声明但未初始化
if (someCondition()) { //根据条件初始化
d = 1
} else {
d = 2
}
println(d) //使用时以被初始化
//因此初始化的时机必须在变量被使用前
}
fun someCondition(): Boolean {
return (System.currentTimeMillis() % 2.0) == 0.0
}
空值安全
Kotlin中的变量类型都是不可以为空的,如果一定要将null
值赋给变量,那么在原来的类型后面加一个?
,将其声明为可空类型,这样,就可以将null
值赋给它了。
package introduction
fun main() {
var neverNull: String = "This can't be null" //声明非空的字符串变量
// neverNull = null //赋空值会报错
var nullable: String? = "You can keep a null here" //声明可空的字符串变量
nullable = null //赋空值不会发生编译错误
var inferredNotNull = "The compiler assumes non-null" //由于字符串已经被初始化,则类型推导认为其不能为空
// inferredNotNull = null //空值会报错
fun strLength(notNull: String): Int { //函数接受的参数被声明为非空类型
return notNull.length
}
strLength(neverNull)
// strLength(nullable) //可空类型不能被接受
println(describeString(null))
println(describeString(""))
println(describeString("apple"))
}
fun describeString(maybeString: String?): String { //接受可空的字符串
if (maybeString != null && maybeString.isNotEmpty()) {
return "String of length is ${maybeString.length}"
} else {
return "Empty or null string"
}
}
类
一个类的声明包括其类名、头部(指明接受的参数类型、主构造器等)、大括号包括的类体。其中,类头和类体都是可选的,根据需要进行类的定义。以下例子体现了类的一些基本用法:
package introduction
//声明一个类,无任何属性,也没有用户自定义的构造方法,
//Kotlin在这样的情况下分配一个无参的默认构造方法Customer()
class Customer
//声明一个类,含有两个属性,并且这两个属性组成构造方法
class Contact(val id: Int, var email: String) //
fun main() {
val customer = Customer() //创建了一个Customer类的实例,使用默认构造法方法
val contact = Contact(1, "mary@gmail.com") //创建Contact类的实例,需要两个参数
println(contact.id) //访问属性,相当于get方法
contact.email = "jane@gmail.com" //更新可变类型的属性,相当于set方法
}
泛型
泛型可以增加一些封装逻辑的重用性。对应的方法或是类使用一个占位符进行定义,然后可以将其替换成自己想要的数据类型,也增强了所谓通用的思想。
泛型类
泛型类使用如下,MutableStack
是一个泛型类,尖括号中的E可以被具体的类型取代,这让我们在设计的时候可以更加关注逻辑,而不需要过于在意具体的类型。
泛型方法
同样地,如果方法对于类型的依赖较小,可以将代码逻辑抽象出来,作为更加通用的泛型方法来使用。
package introduction
class MutableStack<E>(vararg items: E) { //声明了一个泛型类MutableStack<E>,E称作泛型类型参数,在使用时会赋予其明确的类型
private val elements = items.toMutableList()
fun push(element: E) = elements.add(element)
fun peek(): E = elements.last()
fun pop(): E = elements.removeAt(elements.size - 1)
fun isEmpty() = elements.isEmpty()
fun size() = elements.size
override fun toString() = "MutableStack(${elements.joinToString()})"
}
fun <E> mutableStackOf(vararg elements: E) = MutableStack(*elements)
fun main() {
val stack = mutableStackOf(0.62, 3.14, 2.7) //编译器可以推导泛型类型
println(stack)
}
继承
在Kotlin中,类是默认为final
类型的。因此,如果想要将一个类作为基类,被其他的类所继承,就需要使用到open
关键字,这样子类才能出得来。
同样地,Kotlin中,方法也是默认为final
的。因此,如果想要在子类中覆写该方法,就需要将方法定义为open
的,这样才好拿过去“参考”,不然对方不开源就没法改。覆写方法时,需要使用override
关键字指明该方法是后来覆写的。
继承的写法,是使用冒号,格式为:子类:父类(),后面括号的意思其实代表调用无参的默认构造器,可以根据情况修改。
package introduction
open class Dog { //Kotlin中的类默认为final,如果需要被继承,需要使用open标识符进行修饰
open fun sayHello() { //方法默认也为final,如果需要被重写,也要使用open标识符
println("wow wow!")
}
}
class Yorkshire: Dog() { //继承写法,指示调用超类的默认构造方法
override fun sayHello() { //覆写方法
println("wif wif!")
}
}
open class Tiger(val origin: String) {
fun sayHello() {
println("A tiger from $origin says: grrhhh!")
}
}
class SiberianTiger: Tiger("Siberia") //含有参数,在继承时传入
open class Lion(val name: String, val origin:String) {
fun sayHello() {
println("$name, the lion from $origin says: graoh!")
}
}
class Asiatic(name: String): Lion(name = name, origin = "India") //构造方法参数name,用于传值
fun main() {
val dog: Dog = Yorkshire()
dog.sayHello()
val tiger: Tiger = SiberianTiger()
tiger.sayHello()
val lion: Lion = Asiatic("Rufo")
lion.sayHello()
}