kotlin基础 - 基本数据类型和基础语法

#### [kotlin官方文档 https://www.kotlincn.net/docs/reference/](https://www.kotlincn.net/docs/reference/) ####

基本类型
1. Boolen
val aBoolean: Boolean = true
val anOtherBoolean: Boolean = false
println(aBoolean)
println(anOtherBoolean)
运行结果:
true
false
2.数值类型
整数 (32位 2147483647 ~ -2147483648)
val anInt: Int = 0xffffff
val anOtherInt: Int = 0b00111111
val maxInt: Int = Int.MAX_VALUE
val minInt: Int = Int.MIN_VALUE
println(anInt)
println(anOtherInt)
println(maxInt)
println(minInt)
运行结果
16777215
63
2147483647
-2147483648
长整数(64位 -9223372036854775808 ~ 9223372036854775807)
val aLong: Long = 1154545454545542545
val anOtherLong: Long = 123
val minLong: Long = Long.MIN_VALUE
val maxLong: Long = Long.MAX_VALUE
println(aLong)
println(anOtherLong)
println(minLong)
println(maxLong)
运行结果
1154545454545542545
123
-9223372036854775808
9223372036854775807
单精度浮点型 (32位 -3.4028235E38 ~3.4028235E38 存在精度问题)
val aFloat:Float = 0.12F
val anOtherFloat:Float = 1E3F
val minFloat:Float = Float.MIN_VALUE
val maxFloat:Float = Float.MAX_VALUE
println(aFloat)
println(anOtherFloat)
println(minFloat)
println(maxFloat)
运行结果
0.12
1000.0
1.4E-45
3.4028235E38
双精度浮点型(64位 -1.7976931348623157E308 ~ 1.7976931348623157E308 存在精度问题)
val aDouble:Double = 0.12
val anOtheDouble:Double = 1E3
val minDouble:Double = Double.MIN_VALUE
val maxDouble:Double = Double.MAX_VALUE
println(aDouble)
println(anOtheDouble)
println(minDouble)
println(maxDouble)
运行结果
0.12
1000.0
4.9E-324
1.7976931348623157E308
短整型 (16位 -32768 ~ 32767)
val aShort:Short = 12152
val anOtheShort:Short = 120
val minShort:Short = Short.MIN_VALUE
val maxShort:Short = Short.MAX_VALUE
println(aShort)
println(anOtheShort)
println(minShort)
println(maxShort)
运行结果
12152
120
-32768
32767
字节 (8位-128 ~ 127)
val aByte:Byte = 121
val anOtheByte:Byte = -12
val minByte:Byte = Byte.MIN_VALUE
val maxByte:Byte = Byte.MAX_VALUE
println(aByte)
println(anOtheByte)
println(minByte)
println(maxByte)
运行结果
121
-12
-128
127
注意: Float.NaN 不是任何数值 不可以和任何数值比较大小和判断相等
比如
println(0 / 0.0F == Float.NaN)
运行结果为
false
在 kotlin中基本类型可以自动完成自动装箱和自动拆箱,也就是说kotlin中的Long是java中Long和long的组合体,、Int、Short、Double、Float以此类推
Char类型
- 字符对应java中的
Character
- 占两个字节,表示一个16位的 unicode 字符
- 使员单引号引起来 比如
'A','0','中','\n'
kotlin中的转义字符
转义字符 | 含义 |
---|---|
\t | 制表符 |
\b | 光标后退一个字符 |
\n | 回车 |
\r | 光标回到行首 |
’ | 单引号 |
" | 双引号 |
\ | 反斜杠 |
$ | 美元符号,kotlin支持美元符号开头的字符串模板 |
基本类型的转换
不同于java , kotlin不支持隐式转换
举例
val age = 25
//需要对应类型变量调用自己的转换函数
val ageLong: Long = age.toLong()
println(aLong)
运行结果
25
kotlin中的字符串
示例
val aString: String = "HelloWord"
val arrayString: String = String(charArrayOf('H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'd'))
println(aString)
println(arrayString)
//kotlin使用 `==` 判断两个字符串变量的值相等
println(aString == arrayString)
//使用 `===` 判断两个字符串的地址是不是相同
println(aString === arrayString)
运行结果
HelloWord
HelloWord
true
false
注意 :
-
==
可以判断两个字符串变量内容是否相同,相当于java的equal
方法 -
===
可以判断两个字符串变量的地址是否相同,相当于java的==
方法
$变量
, 在字符串中可以引用一个变量的值
val num1: Int = 120
val num2: Int = 160
println("$num1 + $num2 = ${num1+num2}")
运行结果
120 + 160 = 280
如果我们就是期望输出
$变量
本身,我们可以借助转义字符来实现\$变量
println("\$num1")
运行结果
$num1
使员工 """ """
定义带有格式的字符串
val formatStr: String = “”"
去年今日此门中,人面桃花相映红。
出自唐代崔护的《题都城南庄》
去年今日此门中,人面桃花相映红。
人面不知何处去,桃花依旧笑春风。
"""
println(formatStr)
运行结果
去年今日此门中,人面桃花相映红。
出自唐代崔护的《题都城南庄》
去年今日此门中,人面桃花相映红。
人面不知何处去,桃花依旧笑春风。
使用 字符串变量.length
获取字符串的长度
val formatStr: String = """
去年今日此门中,人面桃花相映红。
出自唐代崔护的《题都城南庄》
去年今日此门中,人面桃花相映红。
人面不知何处去,桃花依旧笑春风。
"""
println(formatStr.length)
运行结果
104
kotlin中变量的定义
Kotlin 中可以使用 var 与 val 定义变量
比如
//定义一个非空的变量
val name: String = "小明"
var name: String = "小明"
//定义一个可空的变量
val name: String? = null
var name: String? = null
Kotlin 中 var 与 val 定义变量的区别,及使用场景
- 基本数据类型使用
测试代码:
var a: Int = 1
a = 2
printlin(a)
//得到结果
2
val b: Int = 1
b = 2
//运行结果
error: val cannot be reassigned
- 引用数据类型
当对象使用 val 实例化时
val 实例化的对象,其 var 属性还可以进行更改;但是不可以对实例化的对象再赋值
var 实例化的对象没有任何限制
class Language() {
...
var name: String = ""
...
}
var l = Language()
l.name = "Java"
printlin(l.name)
//运行正常
l = Language()
printlin(l.name)
//运行正常
//val定义的属性,可以改变对象的内容,但是不可以改变对象的引用地址
val l2 = Language()
l2.name = "Kotlin"
l2 = Language()
//报错
error: val cannot be reassigned
val 的使用场景
举个例子,我现在定义一个 Button 对象,这个对象指向 layout 中的一个 button,这种情况下使用 val 就有实战意义了,因为这个 Button 变量理论上是不应该再指向其他 button 了,能很好的规避乱赋值的情况;而且还不影响修改 Button 的属性。
所以,原则应该是,尽量使用 val。
kotlin中的类和变量
kotlin中所有的类都是继承自
Any
类的,相当于java 中任何的类都是继承自Object
类的,也就是说Any
在Kotlin中是所有类的基类

类的声明
class Invoice{}
class Empty //如果类没有实体可以省略{ }
构造函数
它跟在类名(与可选的类型参数)后,如果没有注解或可见性修饰符可以省略constructor关键字
class Person constructor(name: String) {}
class Person (name: String ){} //这两个声明是一样的
注意这里相当于java中构造函数和类声明的合体
主构造函数不能包含任何代码块, 初始化的代码可以放到以init关键字为前缀的代码块中。
class Person (val name: String) {
//优先于初始化块初始化
val s = "name $name".also(:: println)
//创建类的同时会指定这段代码块,相当于java中的构造函数
init {
println("init name : $name")
}
//晚于初始化块初始化
val tian = "songtao $name".also(::println)
}
注意:主构造函数中的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器中使用。
在类实例化期间,初始化块和属性初始化代码按照在类中编码的顺序执行。
次构造函数
类也可以声明前缀有 constructor
的次构造函数,如果一个类中有主构造函数那么每一个次构造函数都需要
委托给主构造函数,可以直接委托也可以通过别的次构造函数委托,委托同类构造函数使用this关键字
class Person (val name: String) {
val s = "name $name".also(:: println)
init {
println("init name : $name")
}
val tian = "tian name: $name".also(:: println)
constructor(name: String, age: Int): this(name) {
}
}
创建代码
val xiaoming: Person = Person("小明")
val xiaofang: Person = Person("小芳",22)
注意:初始化块中的代码实际会作为主构造函数的一部分,委托给主构造函数实际上会作为次构造函数的第一条代码执行。因此初始化块都是执行在次构造函数之前,即使没有主构造函数这种委托也会隐式存在。
如果一个类没有任何主次构造函数,它会生成一个不带参数的主构造函数,如果你想你的类没有一个公共的主构造函数,可以用限制符声明它的非公共主构造函数
class Tian private constructor(){}
创建类的实例
kotlin中没有Java的new关键字,实例化类像普通函数一样调用构造函数
val person = Person("tian")
类成员
类的成员包括
- 构造函数和初始化块
- 函数
- 属性
- 嵌套类和内部类
- 对象声明
继承
kotlin中所有的类都有一个共同的超类Any, 和Java中的object一致,没有超类声明的类Any就是它的默认超类,要声明一个超类,我们把类型放到类头的冒号后. kotlin力求显示清晰,与Java不同,kotlin对于公开的超类和可重写的函数都需要用时open关键字来显示指定公开,类似java中的public。
open class Person
class Man : Person
open class Woman(name:String): Person()
class Student(name:String): Woman(name)
class MyView: View{
constructor(context: Context): super(context)
constructor(context: Context, attrs: AttributeSet): super(context, attrs)
}
类继承的特点
总结:
- 提取多个类的共性得到一个更抽象的类,即父类
- 子类拥有父类的全部特征
- 子类可以自定义自己的特征
空类型和智能类型转换
Kotlin 类型安全(对空指针的优化处理)
-
首先我们看下面的一段 java 代码:
public static void main(String[] args){ System.out.println(getName().length()); } public static String getName(){ return null; }
-
如果运行 main 函数,jvm 会报
Exception in thread "main" java.lang.NullPointerException
的错误。相信大家在 codding 的时候稍有不慎就会遇到这个问题,非常让我们头疼。为了安全起见,我们要做大量的空指针判断,我们的时间严重浪费了在这些毫无技术含量的处理上。于是我们的 main 函数变成了:public static void main(String[] args) { String name = getName(); if (null == name) { System.out.println("未获取到 name!"); return; } System.out.println(name.length()); }
-
但是 Kotlin 的出现,挽救了我们处理空指针的时间。同样的我们看下面一段代码:
fun getName(): String { return null // 这种情况在 Kotlin 中是不允许的 }
-
同样是返回一个 String 类型的
getName()
函数,在 Kotlin 中如果返回null
是无法通过编译的。
这样做,很显然避免的了出现空指针的可能性,但是问题又来了,如果getName()
就是可以返回 null 呢?很简单,我们在返回值类型后面加一个 ? 就表示可以返回 null 了,于是代码变成了:fun getName(): String? { return null }
-
那接着问题又来了,这样不是又跟 Java 一样了吗?同样存在空指针的危险:
fun getName(): String? { return null } fun main(args: Array<String>) { println(getName().length) // 编译通不过,getName()有可能为null }
-
由于上面的那段代码编译无法通过,于是只好进行空指针判断了
fun getName(): String? { return null } fun main(args: Array<String>) { var name = getName() if(null==name){ println("未获取到 name!") return } println(name.length) }
-
这样一来,Kotlin 代码和 Java 就一样了,没什么优点了!不!其实上面的代码是可以简化的
fun getName(): String? { return null } fun main(args: Array<String>) { println(getName()?.length) // null }
-
上面的代码的意思就是,如果
getName()
不为空,则打印长度,否则就返回。
如果知道一个值不可能为空,但是在定义的时候定义了有可能为空,而且也不想在判断,该怎么将解决?val aString: String? = "Hello Kotlin" println(aString.length) // 编译错误 这个时候就可以使用强制执行,就是在变量后面加两个 ! val aString: String? = "Hello Kotlin" println(aString!!.length) // 12
总结
代码表达式 | 解释 |
---|---|
val a:String = 变量 | 将一个非空的变量赋值给字符串a,使用a的时候不需要考虑空指针 |
val a:String = 变量? | 将一个可空的变量赋值给字符串a,使用的时候需要考虑空指针,比如 println(a?.length) |
val a:String = 变量? return | 将一个可空的变量赋值给字符串a,如果为空执行:后面的代码 |
val a:String = 变量? return println(a!!.length) | [变量]!! 告诉编译器这个不可能为空,可以强制编译通过 |
任意类型都有可空和不可空两种
val notNull : String = null //错误,赋值不能为空
val nullable : String ?= null //正确,可以为空
notNull.length //正确,不为空的只可以直接使用
nullable.length //错误,可能为空不能直接获取长度
notNull!!.length //正确,强制认定 notNull 部为空
nullable?.length //正确,如果 nullable 为空,返回空
类型转换
对于子父类之间的类型转换
-
先看这样一段 Java 代码
public class Person{ } public class Student extends Person{ public void study(){ System.out.println("我在学习一门新的语言 Kotlin !"); } } public static void main(String[] args){ Person person = new Student(); if(person instanceof Student){ //已经判断过类型了,但是使用的时候还是需要强转一次,不够这能呀 ((Student) person).study(); } }
-
尽管在 main 函数中,对 person 这个对象进行了类型判断,但是在使用的时候还是需要强制转换成 Student 类型,这样是不是很不智能?
同样的情况在 Kotlin 中就变得简单多了fun main(args: Array<String>) { val person: Person = Student() if (person is Student) { //kotlin可以根据程序的上下文自动推测数据类型,这一点还是相当人性化的 person.study() } }
注意:kotlin 中的 is 可以判断一个对象是这个类型,姓党与java的
instanceof
- 在 Kotlin 中,只要对类型进行了判断,就可以直接通过父类的对象去调用子类的函数了
安全的类型转换
-
还是上面的那个例子,如果我们没有进行类型判断,并且直接进行强转,会怎么样呢?
public static void main(String[] args) { Person person = new Person(); ((Student) person).study(); }
结果就只能是 Exception in thread "main" java.lang.ClassCastException
-
那么在 Kotlin 中是不是会有更好的解决方法呢?
val person: Person = Person() //as可以将一个类对象转换成对应的类型,如果转换失败抛出转换失败的异常,加上? 可以避免转换异常 val student:Student? =person as? Student println(student) // null
在转换操作符后面添加一个 ?,就不会把程序 crash 掉了,当转化失败的时候,就会返回一个 null,当然后?的后面可以跟上任意的表达式,比如return
在空类型中的智能转换
-
需要提前了解 Kotlin 类型安全的相关知识(Kotlin 中的类型安全(对空指针的优化处理))
val aString: String? = "Hello Kotlin" if (aString is String) { println(aString.length) }
aString 在定义的时候定义成了有可能为 null,按照之前的写法,我们需要这样写
val aString: String? = "Hello Kotlin"
println(aString?.length)
但是已经进行了是否为 String 类型的判断,所以就一定 不是 空类型了,也就可以直接输出它的长度了
总结
-
java样式的类型转换(不安全 转换失败抛出异常)
val sub : subClass = parent as subClass
-
安全的类型转换(转阿混失败返回null)
val sub : subClass? = parent as? subClass
-
智能类型转换
如果前面的代码判断出了变量的类型,后面的代码不需要转换,可以直接使用
val child: Parent = Child()
if (child is Child)
println(child?.getName())