Android Kotlin开发语言学习笔记
基本数值类型 Byte、Short、Int、Long、Float、Double 等。不同于 Java 的是,字符不属于数值类型,是一个独立的数据类型:
//基本数值类型包括 Byte、Short、Int、Long、Float、Double 等。不同于 Java 的是,字符不属于数值类型,是一个独立的数据类型
var byteValue: Byte = 123
var shortValue: Short = 123
var intValue: Int = 123
var longValue: Long = 123L
var floatValue: Float = 23.23f
var doubleValue: Double = 23.23
变量定义:
var name:String = "张三" //var 可变变量
val age:Int =18 //val 不可变变量,此时对age进行++或--都会报Val cannot be reassigned的错误
初始化变量为null的操作(添加?):
var name:String=null; //这样写会报Null can not be a value of a non-null type String的错误
var name:String? = null; //如果一定要写null,可这样写,添加一个?,意味着后续可以给变量赋值为null
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
//当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。
//当 str 中的字符串内容不是一个整数时, 返回 null:
fun parseInt(str: String): Int? {
// ...
}
条件判断:
val count: Int =18
var msg:String
//条件判断格式1:(if else模式)
if(count<18){
msg="你还是个少年"
}else if (count>=18&&count<30){
msg="你已经成年啦"
}else{
msg="你已经过了而立之年啦"
}
//条件判断格式2:(when模式)
var msg1:String = when {
count<18 -> "未成年"
count>=18&&count<30 -> "成年啦"
else -> "而立"
}
函数定义格式:
//函数的定义
fun getString(count:Int):String{
var msg1:String = when {
count<18 -> "未成年"
count>=18&&count<30 -> "成年啦"
else -> "而立"
}
return msg1
}
//调用函数
val msg = getString(count)
可变长参数vararg的使用:
/**
* vararg 可变长参数字段,用于可变长参数传递
*/
fun vars(vararg v: Int) {
for (vt in v) {
print(vt)
}
}
//调用
vars(1, 2, 3, 4, 5, 6, 7, 8, 9)
字符串模板使用:
/**
* 字符串模板
* $ 表示一个变量名或者变量值
* $varName 表示变量值
* ${varName.fun()} 表示变量的方法返回值
*/
fun strFom(str1:String,str2:String): String{
println("str1 is $str1 ,str2 is $str2")
return str1+str2
}
//调用
println("返回值是: ${strFom("hello ","world")}")
数值类型判断字符is,相当于java中的instanceof:
/**
* 类型判断字符is,相当于java中的instanceof
*/
fun objectType(obj : Any):String?{
when{
obj is String -> return "String"
obj is Int ->return "Int"
else -> return "other"
}
return null
}
//调用:
println("值类型是: ${objectType("hello")}")
for循环中的区间使用:区间 in 与 downTo ,步长step:
/**
* 区间 in 与 downTo ,步长step,
* 区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。
* 区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。
*/
fun testfun() {
//等同于 1 <= i && i <= 10,输出1到10
for (i in 1..10) {
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) {
println(i) // 输出“13”
}
//downTo,值从大到小
for (i in 4 downTo 1 step 2) {
println(i) // 输出“42”
}
// 使用 until 函数排除结束元素
for (i in 1 until 10) {
println(i) // i in [1, 10) 排除了 10
}
}
比较两个数字
Kotlin 中没有基础数据类型,只有封装的数字类型,你每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。数字类型也一样,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。
在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。
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,值相等
类型转换
由于不同的表示方式,较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型。 这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量。
//每种数据类型都有下面的这些方法,可以转化为其它的类型:
//toByte(): Byte
//toShort(): Short
//toInt(): Int
//toLong(): Long
//toFloat(): Float
//toDouble(): Double
//toChar(): Char
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b.toInt() //
数组定义:
//类Array,还有ByteArray, ShortArray, IntArray,用来表示各个类型的数组,省去了装箱操作,因此效率更高,其用法同Array一样
var x: IntArray = intArrayOf(4, 2, 3)
for (value in x) {
println(value) //value是数组的具体值,输出4,2,3
}
for (i in x.indices) {
println(x[i]) //i是数组的下标,从0开始
}
字符串格式模板""" “”",由三个成对双引号包裹,输出字符串原有的格式排版:
val text = """
|多行字符串
|菜鸟教程
|多行字符串
|Runoob
"""
println(text)
//输出后的内容
|多行字符串
|菜鸟教程
|多行字符串
|Runoob
for循环控制:
for 循环可以对任何提供迭代器(iterator)的对象进行遍历
//对数组进行迭代
var x: IntArray = intArrayOf(4, 2, 3)
for (value in x) {
println(value) //value是数组的具体值,输出4,2,3
}
for (i in x.indices) {
println(x[i]) //i是数组的下标,从0开始
}
//对集合进行迭代
var items = listOf("张三","李四","王五")
for (value in items) {
println(value)
}
for (i in items.indices) {
println(items[i])
}
类
class Runoob { // 类名为 Runoob
// 大括号内是类体构成
}
类的成员变量:
类的属性可以用关键字 var 声明为可变的,否则使用只读关键字 val 声明为不可变。
class Runoob {
//必须初始化值
var name: String =""
var url: String =""
var city: String =""
}
类的构造器
Koltin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
//constructor代表主构造器
class Person constructor(firstName: String) {}
如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。
class Person(firstName: String) {
}
实例:
class Person{
var name : String =""
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
var no : Int =0
get() = field
set(value) {
if (value<10){ // 如果传入的值小于 10 返回该值
field = value
}else{ // 如果传入的值大于等于 10 返回 -1
field = -1
}
}
var heiht: Float = 145.4f
private set
}
调用:
var person: Person = Person()
person.name = "wang"
println("lastName:${person.name}")
person.no = 9
println("no:${person.no}")
person.no = 20
println("no:${person.no}")
//输出内容为:
//lastName:WANG
//no:9
//no:-1
主构造器
如果构造器有注解,或者有可见度修饰符,这时constructor关键字是必须的,注解和修饰符要放在它之前。
实例
创建一个 Runoob类,并通过构造函数传入网站名:
class Runoob constructor(name:String) {
// 大括号内是类体构成
var url: String = "http://www.runoob.com"
var country: String = "CN"
var siteName = name
init {
println("初始化网站名: ${name}")
}
fun printTest() {
println("我是类的函数")
}
}
//调用:
val runoob = Runoob("菜鸟教程")
println(runoob.siteName)
println(runoob.url)
println(runoob.country)
runoob.printTest()
输出结果:
次构造函数
类也可以有二级构造函数,需要加前缀 constructor。
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:
class Person(val name: String) {
constructor (name: String, age:Int) : this(name) {
// 初始化...
}
}
次级构造函数实例:
class Student constructor(name : String) {
// 大括号内是类体构成
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("我是类的函数")
}
}
//调用:
val student = Student("菜鸟教程",1)
println(student.siteName)
println(student.url)
println(student.country)
println(student.printTest())
输出结果:
嵌套类:
类嵌套在其他类中,看以下实例:
class Outer { //外部类
private var bar: Int = 1;
class Nested{ //嵌套类
fun foo()=2
}
}
//调用:
var demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println("调用嵌套类的成员函数:"+demo)
内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,其中 @label 是一个 代指 this 来源的标签。
class Outer { //外部类
private var bar: Int = 1
private var v = "成员属性"
inner class Inner{ /**嵌套内部类**/
fun foo()=bar // 访问外部类成员
fun innerTest(){
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:"+o.v)
}
}
}
//调用:
var demo = Outer().Inner().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo)
var demo1 = Outer().Inner().innerTest()
println(demo1) // 内部类可以引用外部类的成员,例如:成员属性
类的继承
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:
class Example // 从 Any 隐式继承
如果一个类要被继承,可以使用 open 关键字进行修饰。
open class Base(p: Int) // 定义基类
class Derived(p: Int) : Base(p)
子类有主构造函数
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。:
open class Person(var name:String,var age:Int){ //基类,带属性成员name和age
}
class Student (name:String,age:Int,var no : String,var score: Int) : Person(name,age){ //子类
}
//调用:
val s = Student("Runoob",18,"S123456",80)
println("姓名:${s.name}")
println("年龄:${s.age}")
println("学号:${s.no}")
println("成绩:${s.score}")
子类没有主构造函数
如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。
class Student : Person {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}
实例:
/**
* 用户基类
*/
open class Person(var name:String){
/**
* 次级构造函数
*/
constructor(name: String,age:Int):this(name){
//初始化
println("-------基类次级构造函数---------")
}
}
/**
* 子类继承 Person类
*/
class Student : Person{
/**
* 次级构造函数
*/
constructor(name: String,age:Int,no:String,score:Int):super(name,age){
println("-------继承类次级构造函数---------")
println("学生名: ${name}")
println("年龄: ${age}")
println("学生号: ${no}")
println("成绩: ${score}")
}
}
//调用:
Student("Runoob",18,"S123456",80)
输出:
重写
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词
/**
* 用户基类
*/
open class Person{
open fun study(){ //open修饰 允许子类重写
println("正在学习")
}
}
/**
* 子类继承 Person类
*/
class Student : Person() {
override fun study() { //override重写父类方法
super.study() //此处代码会先调用父类方法
println("我毕业啦")
}
}
//调用:
val student = Student()
student.study()
输出:
如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。
open class A {
open fun f(){
println("A")
}
fun a(){
println("a")
}
}
interface B {
fun f(){
println("B") //接口的成员变量默认是open的
}
fun b(){
println("b")
}
}
class C():A(),B {
override fun f() {
super<A>.f() //调用 A.f()
super<B>.f() //调用 B.f()
}
}
//调用:
val c = C()
c.f() //输出 A,B
Kotlin 扩展
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
扩展函数
扩展函数可以在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式:
fun receiverType.functionName(params){
body
}
receiverType:表示函数的接收者,也就是函数扩展的对象
functionName:扩展函数的名称
params:扩展函数的参数,可以为NULL
class User(var name:String){
}
fun User.print(){
println("用户名是: ${name}")
}
//调用:
val user = User("张三")
user.print() //输出 用户名是:张三
案例,为 MutableList 添加一个swap 函数:
// 扩展函数 swap,调换不同位置的值
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this 对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
val l = mutableListOf(4, 2, 3)
// 位置 0 和 2 的值做了互换
l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值
println(l.toString()) //输出[3, 2, 4]
扩展函数是静态解析的
扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的:
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
若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数。
class C {
fun foo() { println("成员函数") }
}
fun C.foo() { println("扩展函数") }
fun main(arg:Array<String>){
var c = C()
c.foo()
}
实例执行输出结果为:
成员函数
扩展一个空对象
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。例如:
fun Any?.toString(): String {
if (this == null) return "null"
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
// 解析为 Any 类的成员函数
return toString()
}
fun main(arg:Array<String>){
var t = null
println(t.toString())
}
实例执行输出结果为:
null
Kotlin 泛型
泛型,即 “参数化类型”,将类型参数化,可以用在类,接口,方法上。
与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
声明一个泛型类:
/**
* 创建box类的泛型
*/
class Box<T>(t:T) {
var value = t
}
实例:
/**
* 创建box类的泛型
*/
class Box<T>(t:T) {
var value = t
}
val boxInt = Box<Int>(10)
val boxString = Box<String>("传入泛型String值")
println(boxInt.value)
println(boxString.value)
输出:
10
传入泛型String值
定义泛型类型变量,可以完整地写明类型参数,如果编译器可以自动推定类型参数,也可以省略类型参数。
Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面:
fun <T> boxIn(value: T) = Box(value)
// 以下都是合法语句
val box4 = boxIn<Int>(1)
val box5 = boxIn(1) // 编译器会进行类型推断
在调用泛型函数时,如果可以推断出类型参数,可以省略泛型参数。
以下实例创建了泛型函数 doPrintln,函数根据传入的不同类型做相应处理:
val age = 23
val value = "runoob"
val bool = true
doPrintln(age) // 整型
doPrintln(value) // 字符串
doPrintln(bool) // 布尔型
/**
* 泛型函数
*/
fun <T> doPrintln(content: T) {
when (content) {
is Int -> println("整型数字为 $content")
is String -> println("字符串转换为大写:${content.toUpperCase()}")
else -> println("T 不是整型,也不是字符串")
}
}
输出:
整型数字为 23
字符串转换为大写:RUNOOB
T 不是整型,也不是字符串
…未完待续