0. 序言
- IDE:IntelliJ Idea。
- 目标:认识基本类型;认识类及其相关概念;认识区间和数组
- Kotlin版本:Version: 1.2.51-release-IJ2018.2-1
- 主要内容:
- Boolean
- Number类型
- 拆箱、装箱
- 字符串
- 转换
- 类和对象
- 空类型
- 类型转换
- 包
- 区间
- 数组
1. Boolean
val aBoolean:Boolean = true // aBoolean 变量的名称 Boolean 变量的类型 true 变量的值
val anotherBoolean:Boolean = false
fun main(args: Array<String>) {
println(aBoolean)
println(anotherBoolean)
}
说明:在java中有boolean和装箱类型Boolean,而Kotlin中只有Boolean,装箱的操作Kotlin会帮忙处理。
2. Number类型
包括Int、Long、Float、Double
val aBoolean:Boolean = true // aBoolean 变量的名称 Boolean 变量的类型 true 变量的值
val anotherBoolean:Boolean = false
val aInt:Int = 8
val anotherInt:Int = 0XFF
val moreInt:Int = 0b00000011
val maxInt:Int = Int.MAX_VALUE
val minInt:Int = Int.MIN_VALUE
val aLong:Long = 12324567899545
val anotherLong:Long = 123
val maxLong:Long = Long.MAX_VALUE
val minLong:Long = Long.MIN_VALUE
val aFloat:Float = 2.0F
val anotherFloat = 1E3F
val maxFloat:Float = Float.MAX_VALUE
val minFloat = - Float.MAX_VALUE
val aDouble:Double = 2.0
val anotherDouble:Double = 3.1415926
val maxDouble:Double = Double.MAX_VALUE
val minDouble:Double = Double.MIN_VALUE
fun main(args: Array<String>) {
println(aBoolean)
println(anotherBoolean)
println(aInt)
println(anotherInt)
println(moreInt)
println(maxInt)
println(minInt)
println(aLong)
println(anotherLong)
println(maxLong)
println(minLong)
println(aFloat)
println(anotherFloat)
println(maxFloat)
println(minFloat)
println(0.0F/0.0F)
}
true
false
8
255
3
2147483647
-2147483648
12324567899545
123
9223372036854775807
-9223372036854775808
2.0
1000.0
3.4028235E38
-3.4028235E38
NaN
说明:NaN指不是数的数,和谁相比都是false,因为没有意义
3. 拆箱、装箱
- 一个基本数据类型(如Int)的变量直接存储了它的值,而一个引用类型(如String)的变量存储的是指向包含该对象的内存地址的引用。
- Java对基本数据类型和引用数据类型做了区分。基本数据类型的值能够更高效地存储和传递,但是不能对这些值调用方法,或是把它们存放在集合中。Java提供了特殊的包装类型(如Integer),在你需要对象的时候对基本数据类型进行封装,就像你不能用Collection<int>来定义一个整数的集合,而必须用Collection<Integer>来定义。
- Kotlin并不区分基本数据类型和包装类型,使用的永远是同一个类型(如Int):
val i:Int = 1
val list:List<Int> = listOf(1,2,3)
1
[1, 2, 3]
说明:Kotlin实际上是做了拆装箱的封装,对于变量、属性、参数和返回类型——Kotlin的Int类型会被编译程Java基本数据类型Int;对于用作泛型类型参数的基本数据类型会被编译成对应的Java包装类型
- Kotlin中能对一个数字类型的值调用方法,如下面这段代码,使用了标准库的函数coerceIn来把值限制在特定范围中:
fun showProgress(progress:Int){
val percent =progress.coerceIn(0,100)
println("We are ${percent}% done!")
}
showProgress(150)
We are 100% done!
- Kotlin对应到Java基本数据类型的类型完整列表如下:
① 整数类型:Byte、Short、Int、Long
② 浮点数类型:Float、Double
③ 字符类型:Char
④ 布尔类型:Boolean
4. 字符串
- 值和地址值比较
val string:String = "HelloWorld"
val fromChars:String = String(charArrayOf('H','e','l','l','o','W','o','r','l','d'))
println(string == fromChars)
println(string === fromChars)
true
false
说明:两个等号"==",在Kotlin中比较的是值。而三个等号"==="比较的是地址值。
- 模板
val arg1:Int = 0
val arg2:Int = 1
println("$arg1 +$arg2 = ${arg1 +arg2}")
说明;用符号{}来引入表达式。
- 转义
val sayHello = "Hello \"MAOZHUXI\""
Hello "MAOZHUXI"
- 三引号用来打印原始字符串(相当于转移以后的字符串):
val rawString:String = """
\t
\n
"""
\t
\n
- 字符串长度
println(sayHello.length)
16
5. 转换
- Kotlin不支持隐式转换:
val A = 1
val B:Long = A
说明:这里IDE会告知错误。所以必须显示转换
val A = 1
val B:Long = A.toLong()
说明:Kotlin为每一种基本数据类型(Boolean除外)都定义有转换函数:toByte()、toShort()、toChar()等,并且支持双向转换,即Long.toInt()或Int.toLong().
- 注意:当你书写数字字面值的时候,一般不需要使用转换函数:有以下四种情况。
① 用特殊的语法来显示地标记常量的类型,比如42L或者42.0f。
② 及时没有用以上特殊语法,当你使用数字字面值去初始化一个类型已知的变量时:
val b:Byte = 1
③ 把字面值作为实参传给函数时,必要的转换会自动地发生:
fun foo(l:Long) = println(l)
foo(42)
42
④ 算术运算符被重载了,可以接收所有适当的数字类型:
val b:Byte = 1
val l = b +1L
println(l)
2
6. 类和对象
- 利用构造方法初始化成员变量
class Man constructor(var name: String, var age: Int){
}
说明:只有一个构造方法的话,constructor也是可以省略的
class Man (var name: String, var age: Int){
}
说明:如果类中没有内容,大括号也是可以省略的
class Man (var name: String, var age: Int)
- 初始化类对象不用new
val mPerson:Man = Man("fukq",28)
说明:当然这里我们可以省略第一个Person类型:
val mPerson = Man("fukq",28)
- init方法:实际上就是构造方法的方法体,每次创建一个对象调用构造方法的时候都会调用init方法
class Man (var name: String, var age: Int){
init {
println("my name is $name ,my age is $age")
}
}
my name is fukq ,my age is 28
- 继承
① 首先定义一个父类
class People(var name:String,var age: Int)
② 让子类继承父类:借助符号":"
class Man (var name: String, var age: Int):People(name,age){
init {
println("my name is $name ,my age is $age")
}
}
说明:用":"继承父类,并把父类的构造方法写出来,并且子类构造方法中的参数类型要省略,因为已经在父类中存在了对应的参数:
class Man(name: String, age: Int) : People(name, age) {
init {
println("my name is $name ,my age is $age")
}
}
说明:除此之外,类默认是final的,所以这个时候我们要声明父类为open:
open class People(var name:String, var age: Int)
- 类名的调用:
open class People(var name: String, var age: Int) {
init {
println("new 了一个${this.javaClass.simpleName}, my name is $name ,my age is $age")
}
}
- 任何类都是Any类的子类:
① 和Object作为Java类层级结构的根差不多,Any类型是Kotlin所有非空类型的超类型(非空类型的根)
② 但是在Java中,Object只是所有引用类型的超类型(引用类型的根),基本数据类型并不是类层级结构的一部分。当你需要Object的时候,不得不把基本数据类型包装成java.lang.Integer这样的包装类型来表示基本数据类型的值。
③ 与Java不同,Kotlin中,Any是所有类型的超类型(所有类型的根),包括像Int这样的基本数据类型。
当然,把基本数据类型的值赋给Any类型的变量时会自动装箱:
var answer:Any = 42
④ Any类型是非空类型的根,所以Any类型的变量不可以持有null值。如果需要持有任何可能值的变量,包括null,必须使用Any?类型。
var answer:Any? = null
说明:如果你不添加?IDE会提示报错
⑤ 在底层:Any类型对应java.lang.Object,Kotlin把Java方法参数和返回类型中用到的Object类型都看做Any类型,所以当Kotlin使用Any类型的时候,它会编译成Java字节码中的Object。
⑥ 所有Kotlin类都包含三个方法:toString、equals和hashCode:这些方法都继承自Any,Any并不能使用其他java.lang.Object的方法(比如wait和notify),但是可以通过手动把值转换程java.lang.Object来调用这些方法。因为Any定义了这三个方法:
public open class Any {
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
}
7. 空类型
- 先看看Java的判空
public class Test_Java {
public static void main(String[] args){
System.out.println(getName().length());
}
private static String getName(){
return null;
}
}
说明:Java中当你调用可能是null的值的方法或者属性的时候,容易因为值是null而报运行时异常:空指针。如何防止呢:
public class Test_Java {
public static void main(String[] args){
String name = getName();
if (name == null){
System.out.println("name不能为空");
}else {
System.out.println(getName().length());
}
}
private static String getName(){
return null;
}
}
说明:Java中你是可以随意的return内容,包括null。
- 再看看Kotlin的判空
fun getName():String{
return null
}
说明:当你在方法中返回一个null值的时候编译器会报错,所以在Kotlin中不能随意return内容,与Java不同。
fun getName():String{
return "fukq"
}
println(getName().length)
说明:在编译期就防止了如空指针这样的运行时异常,让程序更加安全,不会因运行时异常崩溃。
- Kotlin方法返回空类型
① 运用"?"符号来指明这个字符串,即方法返回的结果可以是null
fun getName():String?{
return null
}
② 当方法返回可能是null的时候,我们就要对返回的数据做处理,不然编译会报错:
val name = getName()
if (name ==null){
println("name不能为空")
}else{
println(name.length)
}
说明:进行了空和非空的判断,程序编译无报错,但是Kotlin提供了更简洁的写法:
println(name?.length)
说明:利用安全调用运算符"?.",它允许你把一次null的检查和一次方法调用合并成一个操作。编译期不会报错,它的意思是非空的时候调用方法length,空的时候打印null,不会报空指针异常。很安全,就算给方法返回null,程序也不会crash。
③ 当方法返回是null的时候,执行return或者其他
val name = getName()
if (name ==null) return
println(name.length)
说明:以上是常规写法;Kotlin有方便的Elvis运算符"?:"来处理这种情况
val name = getName() ?: return
println(name.length)
val name = getName() ?: ""
println(name.length)
④ 当你确认可空类型不为null的时候,可使用非空断言“!!"符号:
fun hello(s:String?){
val sNotNUll:String = s!!
println(sNotNUll.length)
}
说明:示例中的s是一个可空类型,但是你确信你传进来的是非空类型,这时候可以使用非空断言"!!"告诉编译器不用检查是否是null。另外你要注意的是假如你判断错误,运行时会抛出空指针异常NullPointerException。所以非空断言告诉编译器:"我知道这个值不为null,如果我错了我准备好了接收这个异常“。
- 综上:
val notNull:String = null //错误,不能为空
val nullable:String?=null //正确,可以为空
notNull.length //正确,不为空的值可以直接使用
nullable.length // 错误,可能为空,不能直接获取长度
nullable!!.length //正确,强制认定nullable不可空,认定错误会报空指针
nullable?.length //正确,若nullable为空,返回空
8. 类型转换安全
- Java 类型转换:
① 定义Parent类:
public class Parent {
}
② 定义Child类:
public class Child extends Parent {
public String getName(){
return "fukq";
}
}
③ 定义类型转换测试类TypeCast:
public class TypeCast {
public static void main(String[] args){
Parent parent = new Child();
((Child) parent).getName();
if (parent instanceof Child){
((Child) parent).getName();
}
}
}
说明:你会发现已经判断parent是Child实例了,但是后面的代码还是要强转才行。
- Kotlin类型智能转换:
val parent:Parent = Child()
if (parent is Child){
println(parent.name)
}
说明:你会发现Kotlin是智能转换,不用强转。
- Kotlin类型利用"as"强转:
val parent = Parent()
val child:Child = parent as Child
println(child.name)
Exception in thread "main" java.lang.ClassCastException: Parent cannot be cast to Child
at Test_Kotlin_01Kt.main(Test_Kotlin_01.kt:135)
说明:那我强转失败,不想转换抛出异常:
val parent = Parent()
val child:Child? = parent as? Child
println(child)
null
说明:这个时候我们收到的就是null,而不是异常。
- 综上:
一旦认定类型,就不用再次强转。
利用as?,类型转换失败返回null而不是异常。
9. 包
这里讲解包主要讲解如何给包指定其他名称,即修改包的命名空间
- 创建包Beijing和Tianjin,里面都有XiaoMing类:
class XiaoMing(var age:Int){
}
- 在Tianjin包里面创建Test类:
package TianJin
fun main(args: Array<String>) {
val xiaoming_tj:XiaoMing = XiaoMing(50)
val xiaoming_bj:Beijing.XiaoMing = Beijing.XiaoMing(60)
}
说明:会发现在Tianjin的包里面,Beijing的XiaoMing要展示全路径代码,因为示例的包结构层次不是很深,实践中的包结构层次如果深,那Beijing的XiaoMing显示全路径的话,代码会变得很长,所以这里我们利用“as"修改它的命名空间:
package TianJin
import Beijing.XiaoMing as BJ_XiaoMing
fun main(args: Array<String>) {
val xiaoming_tj:XiaoMing = XiaoMing(50)
val xiaoming_bj:BJ_XiaoMing =BJ_XiaoMing(60)
}
10. 区间
- 利用".."生成闭区间,返回值类型是IntRange
val range:IntRange = 0..1024 // 闭区间 [0,1024]
- 利用”util“生成右开区间,返回值是IntRange
val range_exclusive:IntRange = 0 until 1024 //右开区间[0,1024)
说明:IntRange继承的是ClosedRange
public class IntRange(start: Int, endInclusive: Int) :
IntProgression(start, endInclusive, 1), ClosedRange<Int> {...}
说明:ClosedRange接口中有两个方法
public interface ClosedRange<T: Comparable<T>> {
public val start: T
public val endInclusive: T
public operator fun contains(value: T): Boolean = value >= start && value <= endInclusive
public fun isEmpty(): Boolean = start > endInclusive
}
说明:
① contains运用:
val range:IntRange = 0..1024 // 闭区间 [0,1024]
println(range.contains(50))
true
② isEmpty()运用
val emptyRange:IntRange = 0..-1
println(emptyRange.isEmpty())
true
③ contains返回一个operator,这个operator对应的运算符号是"in"
println(range.contains(50))
println(50 in range)
说明:以上两句话是等价的。
- 迭代
for (i in range){
println("$i ")
}
- 综上:
区间是ClosedRange的子类,其中IntRange最常用
0..100 表示[0,100]
0 until 100 表示[0,100)
i in 0..100 判断i是否在区间[0,100]中
11. 数组
- 基本数据类型的数组
val arrayOfInt:IntArray = intArrayOf(1,3,5,7)
val arrayOfChar:CharArray = charArrayOf('H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd')
说明:为了避免拆箱装箱带来的性能开销,特设数组定制版
- 引用数据类型的数据
val arrayOfString:Array<String> = arrayOf("I","am","fukq")
val arrayOfPerson:Array<Child> = arrayOf(Child(),Child(),Child())
- 长度
println(arrayOfInt.size) //长度
- 迭代
for(i in arrayOfInt){
println("$i ")
}
- 索引获取元素
println(arrayOfPerson[1])
说明:这样打印出来的内存地址值,想打印字段都信息,可以在类中复写toString方法
class XiaoMing(var age:Int){
override fun toString(): String {
return "XiaoMing的年龄是$age"
}
}
val arrayOfPerson:Array<XiaoMing> = arrayOf(XiaoMing(50),XiaoMing(60),XiaoMing(70))
XiaoMing的年龄是60
- 更改元素值
arrayOfPerson[1] = XiaoMing(100)
- 把字符拼接为字符串
rrayOfChar.joinToString()
H, e, l, l, o, W, o, r, l, d
说明:看下源码
public fun CharArray.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((Char) -> CharSequence)? = null): String {
return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}
说明:其实是一个StringBuilder拼接,默认分隔符是", ",改为空
println(arrayOfChar.joinToString(""))
- 切片
println(arrayOfInt.slice(1..2))
[3, 5]
- 综上:
基本写法:val array:Array<String> = arrayOf(...)
基本操作:输出print array[i];赋值array[i] = "Hello";长度array.size
12. 后续
如果大家喜欢这篇文章,欢迎点赞!
如果想看更多 Kotlin 方面的技术,欢迎关注!