前言
kotlin學習第二篇文章!直接開始!
歷史文章
[AS3.6.1]Kotlin學習筆記1
init
上篇我們講到類的使用JavaC.class
和KotlinC.kt
繼續擴展,首先我們講的是初始化代碼塊,java和kotlin的不同!不太懂可以看下這篇文章Java初始化塊(靜態代碼塊和非靜態代碼塊)
public class JavaC {
private String name = "JavaC";
private List<String> strs;
{
strs = new ArrayList<>();
strs.add("a");
strs.add("b");
strs.add("c");
}
public JavaC() {
}
public JavaC(String name) {
this.name = name;
}
public String getName() {
return name + ".class";
}
public void setName(String name) {
this.name = "set " + name;
}
public void logList(){
for (String str : strs) {
Log.e(name, str);
}
}
}
class KotlinC {
var name: String = "KotlinC"
//由於kotlin使用的是field代表變量所以必須直接寫在變量下方
get() {
return field + ".kt"
}
set(value) {
field = "set " + value
}
var strs: Array<String>? = null
init {
strs = arrayOf("a", "b", "c")
}
constructor()
constructor(name: String) {
this.name = name
}
fun logList(){
for (str in strs!!){
println(str)
}
}
}
還是原來的代碼,就是新增了一個代碼塊!java中直接使用{}
就可以了而在kotlin中需要加入標示init{}
來實現,直接實例化兩個方法調用
javaC.logList() // a b c
println("-------------")
kotlinC.logList() // a b c
都是同樣的結果。這邊再次回顧了下!!
的用法!你可以這麼認爲加上!!
的標示就是讓kotlin的Null安全取消。
常量
在java中我們設置一些常量的時候基本都是使用static final
但是在kotlin中沒有了靜態變量和靜態方法,新的實現如下
//java
public static final String CONSTANT = "JavaC常量";
//kotlin
companion object{
const val CONSTANT:String = "KotlinC常量"
}
//調用
println(JavaC.CONSTANT) //JavaC常量
println(KotlinC.CONSTANT) //KotlinC常量
我們發現kotlin中完全就變了,使用了companion
object
const
一堆的關鍵字修飾一個常量。
object
上述一堆關鍵字我們就從object
開始講解。在java中我們有Object
代表一切類的基類,但是在kotlin中Object
變成了Any
,而object
是一個關鍵字,代表了對象的意思。
我們是可以直接創建一個對象的,即對象類
object Object {
val name = "我是一個對象"
fun log(){
println(name)
}
}
我們發現直接可以看到創建類的class
直接被替換成了object
也可以直接調用
println(Object.name) //我是一個對象
Object.log() //我是一個對象
- 單例類
這邊我們還需要了解到kotlin的object
其實是一個自帶實現餓漢式單例的單例對象
我們來比對下java和kotlin的餓漢式單列代碼
//java
public class JHungry {
private static final JHungry hungry = new JHungry();
private String name = "Java Hungry";
public static JHungry getInstance() {
return hungry;
}
private JHungry() {
}
public String getName() {
return name;
}
}
//kotlin
object KHungry {
var name: String = "Kotlin Hungry";
}
//調用
println(JHungry.getInstance().name) //Java Hungry
println(KHungry.name) //Kotlin Hungry
我們很明顯的可以看到使用kotlin的object
對象直接生成的餓漢式的單例比java簡便了非常的多!
並且object
是可以直接繼承class
和interface
的能讓繼承的類既有 class 關鍵字的功能,又實現了單例。快捷且方便!
- 匿名類
java中我們也會經常用到匿名類,kotlin中也可以實現,下面我們看下代碼區別
//java
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//override
}
};
//Convert to lambda
View.OnClickListener listener2 = v ->{
//override
};
//kotlin
val listener = object: View.OnClickListener {
override fun onClick(v: View?) {
//override
}
}
//Convert to lambda
val listener2 = View.OnClickListener {
//override
}
可以看到由於kotlin沒有new
這個關鍵字所以使用了object
來替代,轉化成lambda的時候java省略了new
kotlin就省略打了object
companion
經過上面object
的說明我們瞭解到只要用了object
生成的對象類就一定使整個類都變成了靜態的,如果我們只想要部分代碼靜態就需要用到companion
代碼就是我們一開始常量目錄下的JavaC類和KotlinC類
//java
public class JavaC {
private String name = "JavaC";
public static final String CONSTANT = "JavaC常量";
//...省略其他代碼
}
//kotlin
class KotlinC {
var name: String = "KotlinC"
companion object Const{
const val CONSTANT:String = "KotlinC常量"
}
//...省略其他代碼
}
我們就可以這麼理解companion
:可以理解爲伴隨、伴生,表示修飾的對象和外部類綁定。
這邊我們爲companion object
設置了一個名字Const
但是我們調用的代碼是可以跳過object
對象的名稱直接調用的
println(JavaC.CONSTANT) //JavaC常量
println(KotlinC.CONSTANT) //KotlinC常量
所以就有了常量目錄下最開始的直接寫法
companion object{
const val CONSTANT:String = "KotlinC常量"
}
top-level property / function 聲明
kotlin中的top-level property
頂層聲明 這可以直接去調用一個kt文件裏面的方法,只需要導入包名就可以使用,非常的方便。
首先我們創建一個expand.kt文件
package com.gjn.kotlindemo
fun funExpand(){
println("fun Expand")
}
裏面就一個方法,那麼我們在同級目錄下的其他類中可以完美的直接使用,不在同級只需要導入包名也是可以直接使用的
package com.gjn.kotlindemo
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
funExpand() //fun Expand
}
}
在java中我們經常會寫一些工具類,在kotlin中就可以使用top-level property
完美簡化工具類的調用,還有就是我們先在經常直接使用的println
方法我們會發現明明我們什麼都都沒導入直接就可以使用系統的System.out.println
了,其實你點進println
就可以看到其實是官方寫了一個kt方法來實現的。
使用object
、companion object
和 top-level
情況
那在實際使用中,在
object
、companion object
和top-level
中該選擇哪一個呢?
簡單來說按照下面這兩個原則判斷:
如果想寫工具類的功能,直接創建文件,寫 top-level「頂層」函數。
如果需要繼承別的類或者實現接口,就用 object 或 companion object。
const
與java的常量有區別,kotlin的常量是有規則的
- 必須聲明在對象
object
(包括伴生對象companion object
)或者「top-level
頂層」中,因爲常量是靜態的。 - 必須加修飾常量的
const
關鍵字。 - 只有基本類型和
String
類型可以聲明成常量。
這邊kotlin和java有一點很大的區別,就是不能把非基本類型設置成常量。這是因爲kotlin中的常量就是代表了在編譯之後百分百不會變動的纔是常量,而很多非基本類都是設置一個僞常量對象,實際運行中是會被修改內部的屬性的,所以屬於僞常量。
我這邊覺一個例子,我們在app運行前並沒有獲取服務器部分數據,拿到json數據之後轉換成常量對象,所以我們會先設置一個常量對象設置null當服務器獲取數據之後在來配置一些基本值。這個時候
我們開始設置的常量對象其實就只是一個僞常量因爲我們獲取到數據之後其實改變了常量對象內的值,所以在kotlin中直接就去掉這個概念,防止一些錯誤產生。
數組、集合、序列
第一篇中講for的調用的時候,稍微寫了些數組和集合。下面我們來正式講一下kotlin的數組和集合
數組
首先我們看下java和kotlin創建數組的區別
//java
private String[] arrayStrs = {"java1", "java2", "java3"};
//kotlin
val arrayStrs: Array<String> = arrayOf("kotlin1", "kotlin2", "kotlin3")
我們可以看到kotlin中的數組其實是一個對象是可以直接調用一些常用的方法的,爲了更加便捷。
取值和修改和java中是一樣的
println(kotlinC.arrayStrs[1]) //kotlin2
kotlinC.arrayStrs[0] = "test"
println(kotlinC.arrayStrs[0]) //test
集合
kotlin和java一樣有三種集合類型:List、Set 和 Map。
和創建數組一樣集合也是可以快速創建的
//java
private List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("b");
strList.add("c");
strList.add("b");
strList.add("a");
private Set<String> strSet = new HashSet<>();
strSet.add("a");
strSet.add("b");
strSet.add("c");
strSet.add("b");
strSet.add("a");
private Map<Integer, String> strMap = new HashMap<>();
strMap.put(1, "a");
strMap.put(2, "b");
strMap.put(3, "c");
strMap.put(4, "b");
strMap.put(5, "a");
//kotlin
val strList = listOf("a", "b" , "c", "b" , "a")
val strSet = setOf("a", "b" , "c", "b" , "a")
val strMap = mapOf(1 to "a", 2 to "b", 3 to "c", 4 to "b", 5 to "a")
我們能夠看到,kotlin創建數組添加數據比java方便了許多。
這邊要注意一下使用listOf
setOf
mapOf
生成的集合是隻讀集合,是無法修改值的。想要修改值可以使用toMutableList
toMutableSet
toMutableMap
方法或者直接創建一個mutableListOf
mutableSetOf
mutableMapOf
集合,可以發現可變的集合都是使用mutable
前綴修飾
序列(Sequence)
這是kotlin引入的新容器。它和Iterable
一樣用來遍歷一組數據並可以對每個元素進行特定的處理。
基本使用如下
val strSeq =sequenceOf("a", "b", "c", "b", "a")
//strList是上面創建的 基本上最常用的創建方法
val strSeq2 = strList.asSequence()
//generateSequence按方法推斷生成 直到返回結果爲null
//無限序列
val strSeq3 = generateSequence(0){ it + 1}
//有限序列
val strSeq4 = generateSequence(0){
if (it < 10){
it + 1
}else{
null
}
}
那麼問題來了,這個有啥用呢?我們可以把序列簡單理解爲Java8中的流,序列簡單一點說就是實現對集合的操作進行延遲,專業的說法叫惰性集合操作,就是把你對集合的一系列操作拖到最後一刻才執行。序列和集合的區別就是惰性求值
和及早求值
的區別,集合會在每次形式新的集合的時候全部遍歷一遍,而序列是執行完後續步驟之後纔會走下一個元素,不是很明白可以看下Kotlin系列之序列
修飾符
public
:公開,哪都可以引用private
:私有,聲明類或者文件內引用protected
:保護,在kotlin中可以理解爲private
+子類引用internal
:內部,kotlin新增加的修飾符,僅在module內引用
這邊我們說下 private
和internal
。
首先private
和java中有點區別,就是private
的範圍更小,在一個外部類新增一個內部類,如果對內部類的參數或者函數設置了private
,那麼在外部類也無法直接訪問或者調用內部類的參數和函數,這是和java的區別。
然後是internal
,這是kotlin新加的修飾符,可以理解爲項目內可用,比如我們寫一個某個工具library這個時候裏面加入internal
的函數就只能在library內使用,我們外部導入這個工具library是無法調用設置internal
的函數或者參數的。