
0. 序言
- IDE:IntelliJ Idea。
- 目標(biāo):認(rèn)識基本類型;認(rèn)識類及其相關(guān)概念;認(rèn)識區(qū)間和數(shù)組
- Kotlin版本:Version: 1.2.51-release-IJ2018.2-1
- 主要內(nèi)容:
- Boolean
- Number類型
- 拆箱、裝箱
- 字符串
- 轉(zhuǎn)換
- 類和對象
- 空類型
- 類型轉(zhuǎn)換
- 包
- 區(qū)間
- 數(shù)組
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指不是數(shù)的數(shù),和誰相比都是false,因為沒有意義
3. 拆箱、裝箱
- 一個基本數(shù)據(jù)類型(如Int)的變量直接存儲了它的值,而一個引用類型(如String)的變量存儲的是指向包含該對象的內(nèi)存地址的引用。
- Java對基本數(shù)據(jù)類型和引用數(shù)據(jù)類型做了區(qū)分?;緮?shù)據(jù)類型的值能夠更高效地存儲和傳遞,但是不能對這些值調(diào)用方法,或是把它們存放在集合中。Java提供了特殊的包裝類型(如Integer),在你需要對象的時候?qū)緮?shù)據(jù)類型進行封裝,就像你不能用Collection<int>來定義一個整數(shù)的集合,而必須用Collection<Integer>來定義。
- Kotlin并不區(qū)分基本數(shù)據(jù)類型和包裝類型,使用的永遠(yuǎn)是同一個類型(如Int):
val i:Int = 1
val list:List<Int> = listOf(1,2,3)
1
[1, 2, 3]
說明:Kotlin實際上是做了拆裝箱的封裝,對于變量、屬性、參數(shù)和返回類型——Kotlin的Int類型會被編譯程Java基本數(shù)據(jù)類型Int;對于用作泛型類型參數(shù)的基本數(shù)據(jù)類型會被編譯成對應(yīng)的Java包裝類型
- Kotlin中能對一個數(shù)字類型的值調(diào)用方法,如下面這段代碼,使用了標(biāo)準(zhǔn)庫的函數(shù)coerceIn來把值限制在特定范圍中:
fun showProgress(progress:Int){
val percent =progress.coerceIn(0,100)
println("We are ${percent}% done!")
}
showProgress(150)
We are 100% done!
- Kotlin對應(yīng)到Java基本數(shù)據(jù)類型的類型完整列表如下:
① 整數(shù)類型:Byte、Short、Int、Long
② 浮點數(shù)類型: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}")
說明;用符號{}來引入表達式。
- 轉(zhuǎn)義
val sayHello = "Hello \"MAOZHUXI\""
Hello "MAOZHUXI"
- 三引號用來打印原始字符串(相當(dāng)于轉(zhuǎn)移以后的字符串):
val rawString:String = """
\t
\n
"""
\t
\n
- 字符串長度
println(sayHello.length)
16
5. 轉(zhuǎn)換
- Kotlin不支持隱式轉(zhuǎn)換:
val A = 1
val B:Long = A
說明:這里IDE會告知錯誤。所以必須顯示轉(zhuǎn)換
val A = 1
val B:Long = A.toLong()
說明:Kotlin為每一種基本數(shù)據(jù)類型(Boolean除外)都定義有轉(zhuǎn)換函數(shù):toByte()、toShort()、toChar()等,并且支持雙向轉(zhuǎn)換,即Long.toInt()或Int.toLong().
- 注意:當(dāng)你書寫數(shù)字字面值的時候,一般不需要使用轉(zhuǎn)換函數(shù):有以下四種情況。
① 用特殊的語法來顯示地標(biāo)記常量的類型,比如42L或者42.0f。
② 及時沒有用以上特殊語法,當(dāng)你使用數(shù)字字面值去初始化一個類型已知的變量時:
val b:Byte = 1
③ 把字面值作為實參傳給函數(shù)時,必要的轉(zhuǎn)換會自動地發(fā)生:
fun foo(l:Long) = println(l)
foo(42)
42
④ 算術(shù)運算符被重載了,可以接收所有適當(dāng)?shù)臄?shù)字類型:
val b:Byte = 1
val l = b +1L
println(l)
2
6. 類和對象
- 利用構(gòu)造方法初始化成員變量
class Man constructor(var name: String, var age: Int){
}
說明:只有一個構(gòu)造方法的話,constructor也是可以省略的
class Man (var name: String, var age: Int){
}
說明:如果類中沒有內(nèi)容,大括號也是可以省略的
class Man (var name: String, var age: Int)
- 初始化類對象不用new
val mPerson:Man = Man("fukq",28)
說明:當(dāng)然這里我們可以省略第一個Person類型:
val mPerson = Man("fukq",28)
- init方法:實際上就是構(gòu)造方法的方法體,每次創(chuàng)建一個對象調(diào)用構(gòu)造方法的時候都會調(diào)用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")
}
}
說明:用":"繼承父類,并把父類的構(gòu)造方法寫出來,并且子類構(gòu)造方法中的參數(shù)類型要省略,因為已經(jīng)在父類中存在了對應(yīng)的參數(shù):
class Man(name: String, age: Int) : People(name, age) {
init {
println("my name is $name ,my age is $age")
}
}
說明:除此之外,類默認(rèn)是final的,所以這個時候我們要聲明父類為open:
open class People(var name:String, var age: Int)
- 類名的調(diào)用:
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類層級結(jié)構(gòu)的根差不多,Any類型是Kotlin所有非空類型的超類型(非空類型的根)
② 但是在Java中,Object只是所有引用類型的超類型(引用類型的根),基本數(shù)據(jù)類型并不是類層級結(jié)構(gòu)的一部分。當(dāng)你需要Object的時候,不得不把基本數(shù)據(jù)類型包裝成java.lang.Integer這樣的包裝類型來表示基本數(shù)據(jù)類型的值。
③ 與Java不同,Kotlin中,Any是所有類型的超類型(所有類型的根),包括像Int這樣的基本數(shù)據(jù)類型。
當(dāng)然,把基本數(shù)據(jù)類型的值賦給Any類型的變量時會自動裝箱:
var answer:Any = 42
④ Any類型是非空類型的根,所以Any類型的變量不可以持有null值。如果需要持有任何可能值的變量,包括null,必須使用Any?類型。
var answer:Any? = null
說明:如果你不添加?IDE會提示報錯
⑤ 在底層:Any類型對應(yīng)java.lang.Object,Kotlin把Java方法參數(shù)和返回類型中用到的Object類型都看做Any類型,所以當(dāng)Kotlin使用Any類型的時候,它會編譯成Java字節(jié)碼中的Object。
⑥ 所有Kotlin類都包含三個方法:toString、equals和hashCode:這些方法都繼承自Any,Any并不能使用其他java.lang.Object的方法(比如wait和notify),但是可以通過手動把值轉(zhuǎn)換程java.lang.Object來調(diào)用這些方法。因為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中當(dāng)你調(diào)用可能是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內(nèi)容,包括null。
- 再看看Kotlin的判空
fun getName():String{
return null
}
說明:當(dāng)你在方法中返回一個null值的時候編譯器會報錯,所以在Kotlin中不能隨意return內(nèi)容,與Java不同。
fun getName():String{
return "fukq"
}
println(getName().length)
說明:在編譯期就防止了如空指針這樣的運行時異常,讓程序更加安全,不會因運行時異常崩潰。
- Kotlin方法返回空類型
① 運用"?"符號來指明這個字符串,即方法返回的結(jié)果可以是null
fun getName():String?{
return null
}
② 當(dāng)方法返回可能是null的時候,我們就要對返回的數(shù)據(jù)做處理,不然編譯會報錯:
val name = getName()
if (name ==null){
println("name不能為空")
}else{
println(name.length)
}
說明:進行了空和非空的判斷,程序編譯無報錯,但是Kotlin提供了更簡潔的寫法:
println(name?.length)
說明:利用安全調(diào)用運算符"?.",它允許你把一次null的檢查和一次方法調(diào)用合并成一個操作。編譯期不會報錯,它的意思是非空的時候調(diào)用方法length,空的時候打印null,不會報空指針異常。很安全,就算給方法返回null,程序也不會crash。
③ 當(dāng)方法返回是null的時候,執(zhí)行return或者其他
val name = getName()
if (name ==null) return
println(name.length)
說明:以上是常規(guī)寫法;Kotlin有方便的Elvis運算符"?:"來處理這種情況
val name = getName() ?: return
println(name.length)
val name = getName() ?: ""
println(name.length)
④ 當(dāng)你確認(rèn)可空類型不為null的時候,可使用非空斷言“!!"符號:
fun hello(s:String?){
val sNotNUll:String = s!!
println(sNotNUll.length)
}
說明:示例中的s是一個可空類型,但是你確信你傳進來的是非空類型,這時候可以使用非空斷言"!!"告訴編譯器不用檢查是否是null。另外你要注意的是假如你判斷錯誤,運行時會拋出空指針異常NullPointerException。所以非空斷言告訴編譯器:"我知道這個值不為null,如果我錯了我準(zhǔn)備好了接收這個異常“。
- 綜上:
val notNull:String = null //錯誤,不能為空
val nullable:String?=null //正確,可以為空
notNull.length //正確,不為空的值可以直接使用
nullable.length // 錯誤,可能為空,不能直接獲取長度
nullable!!.length //正確,強制認(rèn)定nullable不可空,認(rèn)定錯誤會報空指針
nullable?.length //正確,若nullable為空,返回空
8. 類型轉(zhuǎn)換安全
- Java 類型轉(zhuǎn)換:
① 定義Parent類:
public class Parent {
}
② 定義Child類:
public class Child extends Parent {
public String getName(){
return "fukq";
}
}
③ 定義類型轉(zhuǎn)換測試類TypeCast:
public class TypeCast {
public static void main(String[] args){
Parent parent = new Child();
((Child) parent).getName();
if (parent instanceof Child){
((Child) parent).getName();
}
}
}
說明:你會發(fā)現(xiàn)已經(jīng)判斷parent是Child實例了,但是后面的代碼還是要強轉(zhuǎn)才行。
- Kotlin類型智能轉(zhuǎn)換:
val parent:Parent = Child()
if (parent is Child){
println(parent.name)
}
說明:你會發(fā)現(xiàn)Kotlin是智能轉(zhuǎn)換,不用強轉(zhuǎn)。
- Kotlin類型利用"as"強轉(zhuǎn):
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)
說明:那我強轉(zhuǎn)失敗,不想轉(zhuǎn)換拋出異常:
val parent = Parent()
val child:Child? = parent as? Child
println(child)
null
說明:這個時候我們收到的就是null,而不是異常。
- 綜上:
一旦認(rèn)定類型,就不用再次強轉(zhuǎn)。
利用as?,類型轉(zhuǎn)換失敗返回null而不是異常。
9. 包
這里講解包主要講解如何給包指定其他名稱,即修改包的命名空間
- 創(chuàng)建包Beijing和Tianjin,里面都有XiaoMing類:
class XiaoMing(var age:Int){
}
- 在Tianjin包里面創(chuàng)建Test類:
package TianJin
fun main(args: Array<String>) {
val xiaoming_tj:XiaoMing = XiaoMing(50)
val xiaoming_bj:Beijing.XiaoMing = Beijing.XiaoMing(60)
}
說明:會發(fā)現(xiàn)在Tianjin的包里面,Beijing的XiaoMing要展示全路徑代碼,因為示例的包結(jié)構(gòu)層次不是很深,實踐中的包結(jié)構(gòu)層次如果深,那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. 區(qū)間
- 利用".."生成閉區(qū)間,返回值類型是IntRange
val range:IntRange = 0..1024 // 閉區(qū)間 [0,1024]
- 利用”util“生成右開區(qū)間,返回值是IntRange
val range_exclusive:IntRange = 0 until 1024 //右開區(qū)間[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 // 閉區(qū)間 [0,1024]
println(range.contains(50))
true
② isEmpty()運用
val emptyRange:IntRange = 0..-1
println(emptyRange.isEmpty())
true
③ contains返回一個operator,這個operator對應(yīng)的運算符號是"in"
println(range.contains(50))
println(50 in range)
說明:以上兩句話是等價的。
- 迭代
for (i in range){
println("$i ")
}
- 綜上:
區(qū)間是ClosedRange的子類,其中IntRange最常用
0..100 表示[0,100]
0 until 100 表示[0,100)
i in 0..100 判斷i是否在區(qū)間[0,100]中
11. 數(shù)組
- 基本數(shù)據(jù)類型的數(shù)組
val arrayOfInt:IntArray = intArrayOf(1,3,5,7)
val arrayOfChar:CharArray = charArrayOf('H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd')
說明:為了避免拆箱裝箱帶來的性能開銷,特設(shè)數(shù)組定制版
- 引用數(shù)據(jù)類型的數(shù)據(jù)
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])
說明:這樣打印出來的內(nèi)存地址值,想打印字段都信息,可以在類中復(fù)寫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拼接,默認(rèn)分隔符是", ",改為空
println(arrayOfChar.joinToString(""))
- 切片
println(arrayOfInt.slice(1..2))
[3, 5]
- 綜上:
基本寫法:val array:Array<String> = arrayOf(...)
基本操作:輸出print array[i];賦值array[i] = "Hello";長度array.size
12. 后續(xù)
如果大家喜歡這篇文章,歡迎點贊!
如果想看更多 Kotlin 方面的技術(shù),歡迎關(guān)注!