Kotlin

一、基础语法

Kotlin中可变变量、只读变量、静态变量、常量
格式:修饰符 名称:类型 = 默认值

var num: Int = 10

空安全的声明方式

var str: String ?= null

可变变量:
var: var是一个可变变量,这是一个可以通过重新分配来更改为另一个值的变量,这种声明变量的方式和Java中声明变量的方式一样。

特点:
可以重写set和get方法,注意在set和get方法中,变量用field表示,不能用变量本身,否则会出现循环调用。

var num2 = 10
    set(value) {
        field = value + 5
    }

    get() {
        return field + 5
    }

只读变量:
val: val是一个只读变量,这种声明变量的方式相当于java中的final变量,一个val创建的时候必须初始化,因为以后不能被改变。

特点:
只能重写get方法,不能重写set方法

val num3 = 10
    get() {
        return field + 5
    }

静态变量:
Kotlin中声明静态变量的方法是将对象放在对象中声明。

/**
*  静态类
*/
object StaticData {
    /**
     * 静态变量(private)
     */
    var num6 = 10

}

如果把变量放到一个普通对象中,声明出来的变量是私有的,外部调用不到,推荐使用伴生对象来声明静态变量。

/**
* 伴生对象
*/
companion object {
    /**
     * 静态变量
     */

    var num4 = 10

}

伴生对象相当于是外部类的对象,我们可以使用类直接调用,在伴生对象中声明的变量,实际上编译成java代码后对象都声明在了伴生对象的外部类里面了,伴生对象里面只是生成的set和get方法。

常量:
常量值是在编译时期就确定下来的,因此常量值可以直接赋值,也可以赋值为其他常量值,但不能赋值为非常量值,即不可以用没有被const修饰的变量给它赋值,
const只能修饰val,不能修饰var。
const val 常量值的名字 = 常量值

声明一个常量可以在伴生对象中声明

object StaticData {
    /**
     * 常量:const val
     */
    const val num7 = 10

}

也可以在类外面声明,用这种方法声明的常量,相当与创建了一个 类名称 + Kt(KotlinTestActivitykt)的对象,常量属于这个对象

const val num8 = 10

open class KotlinTestActivity : BaseActivity(), View.OnClickListener

二、Kotlin中的方法

Kotlin中声明方法的格式:

fun  [方法名] ( [参数名] : [参数类型] ) : [返回类型]{
    ...
    return [返回值]
}

有返回值:

fun add(num1: Int, num2: Int): Int {
    return num1 + num2
}

无返回值,Unit代表为空,可以省略:

fun log(msg: String): Unit {
    print(msg)
}

fun log(msg: String) {
    print(msg)
}

静态方法:需要声明在对象中object

/**
* 伴生对象
*/
companion object {
    /**
     * 静态方法
     */

    fun getData(): Int {
        return 1
    }

    /**
     * 静态方法
     */
    @JvmStatic
    fun getNewData(): Int {
        return 2
    }
}

在Java中调用需要加companion

KotlinTestActivity.Companion.getNum4()

可以通过注解@JvmStatic省略Companion直接调用

KotlinTestActivity.getNewData()

Kotlin中的方法重载:

Java中不同参数,类型的方法重写需要写多个方法,在Kotlin中只需要声明一个方法就可以解决。

/**
* @param arg1 必传
* @param arg2 必传
* @param arg3 必传
* @param arg4 可不传,不传时默认值 2
* @param arg5 可不传,不传时默认值 test
*/

fun test(arg1: String?, arg2: Int?, arg3: String?, arg4: Int? = 2, arg5: String? = "test") {

}

方法中的参数可以设置默认值,在外部没有传值得情况就可以用默认值来代替,上面那个方法中后两个参数有默认值,所有在调用这个方法的时候是可以不传那两个参数的,如果参数没有默认值,是必传的。

test("arg1", 2, "arg3", 4, "arg5")

test("arg1", 2, "arg3")

Kotlin方法中的参数也可以不一设定的顺序传递,需要指定传递的哪个参数

test(arg1 = "arg1", arg2 = 2, arg3 = "arg3")

test(arg3 = "arg3", arg1 = "arg1", arg2 = 2)

三、Kotlin中的null安全

Kotlin将变量分为可以为Nullable类型和Non-Null类型,变量默认Non-Null类型,如果想声明Nullable的变量,需要用“?”修饰,

加?表示该变量可能为空,不加则表示一定不会指向一个空引用。

声明Nullable类型变量

var name: String? = null

声明Non-Null类型变量

var name1: String = ""

name1可以直接赋值给name

name = name1

但是name要赋值给name1,必须加!!

name1 = name!!

如果name为空,就会抛出KotlinNullPointerException异常,所以Nullable类型变量要赋值给Non-Null类型变量时,要先判断是否为空,不为空才可以赋值,并且不建议使用!!。

四、Kotlin中的 data class

在 Kotlin 中,不需要自己动手去写一个 JavaBean,可以直接使用 DataClass,使用 DataClass 编译器会默默地帮我们生成以下方法

set()
get()
equals()
hashCode()
toString()
componentN()
copy()

定义一个 data class 类:

data class UserData(
        var name: String,
        var age: Int = 20,
        var avatar: String? = null,
        var userInfo: UserInfo? = null

)

虽然data class为我们生成了很多方法,减少了我们的很多代码量,但是data class 存在两个问题,没有无参数的构造方法,而且是被final修饰不能被继承,不过可以利用官方给出的插件来解决这些问题(noarg、allopen)。

https://www.jianshu.com/p/90a3233b0a8a?utm_campaign=maleskine&utm_content=note&utm_medium=reader_share&utm_source=weibo

buildscript {
    dependencies {
        classpath "org.jetbrains.[kotlin:kotlin-noarg:$kotlin_version](http://kotlinkotlin-noarg%24kotlin_version/)"
        classpath "org.jetbrains.[kotlin:kotlin-allopen:$kotlin_version](http://kotlinkotlin-allopen%24kotlin_version/)"
    }
}

通过插件可以帮我们去掉class的final关键字,并且生成一个无参的构造方法,但是由于是在编译器做的操作,所以在源代码中还是无法直接使用无参的构造函数,只能通过反射来使用。

@KotlinData
data class OneData(var arg: String)

class NewData(var arg2: String, var arg3: Int, arg: String): OneData(arg)

如果需要无参的构造方法,可以给每个变量都设置初始默认,或者采用一般的class

data class UserInfo(
        var info1: String? = null,
        var info2: String? = null,
        var info3: Int = 0

)

一般的class

class OtherInfo {
    var info1: String? = null
    var info2: String? = null
    var info3: Int = 0
}

五、Kotlin中扩展函数

扩展函数实际上就是一个对应Java中的静态函数,这个静态函数参数为接收者类型的对象,然后利用这个对象就可以访问这个类中的成员属性和方法了,并且最后返回一个这个接收者类型对象本身。这样在外部感觉和使用类的成员函数是一样的,它并没有改变类本身。

扩展函数的使用:

只需要把扩展的类或者接口名称,放到即将要添加的函数名前面。这个类或者名称就叫做接收者类型,类的名称与函数之间用"."调用连接。this指代的就是接收者对象,它可以访问扩展的这个类可访问的方法和属性。

fun test(str: String): String {
    return "back$str"
}

fun TextView.setColor(colorRes: Int) {
    this.setTextColor(context.getColor(colorRes))
}

fun ImageView.loadImage(drawableRes: Int) {
    Glide.with(this)
            .load(drawableRes)
            .into(this)

}

在外面调用:

var test = getBack("test")
tv_age?.setColor(R.color.color_4)
tv_avatar?.loadImage(R.drawable.head_bg_img)

Kotlin扩展函数允许我们在不改变已有类的情况下,为类添加新的函数,在java要调用扩展函数要将被扩展的对象传进去。

KotlinExtensionKt.getBack("test");
KotlinExtensionKt.setColor(tv, R.color.color_0);
KotlinExtensionKt.loadImage(iv, R.drawable.head_bg_img);

六、Kotlin中的Lambda表达式和高阶函数

1、Lambda表达式的本质其实是匿名函数,因为在其底层实现中还是通过匿名函数来实现的。
2、将函数作为另一个函数的参数或者返回值的函数是高阶函数

语法:

1\. 无参数的情况 :
    val/var 变量名 = { 操作的代码 }

2\. 有参数的情况     
    val/var 变量名 : (参数的类型,参数类型,...) -> 返回值类型 = {参数1,参数2,... -> 操作参数的代码 }
    可等价于
    // 此种写法:即表达式的返回值类型会根据操作的代码自推导出来。
    val/var 变量名 = { 参数1 : 类型,参数2 : 类型, ... -> 操作参数的代码 }

3\. lambda表达式作为函数中的参数的时候,这里举一个例子:
    fun test(a : Int, 参数名 : (参数1 : 类型,参数2 : 类型, ... ) -> 表达式返回类型){  
       ...  
   }

特点:
1、Lambda表达式总是被大括号括着
2、其参数(如果存在)在 -> 之前声明(参数类型可以省略)
3、函数体(如果存在)在 -> 后面。
举例:

1、无参数的情况

 // 源代码
 fun test() { 
    println("无参数") 
 }

 // lambda代码
 val test = { 
    println("无参数") 
 }

 // 调用
 test()  => 结果为:无参数

2、有参数的情况
 // 源代码
 fun test(a : Int , b : Int) : Int{
     return a + b
 }

 // lambda
 val test : (Int , Int) -> Int = {a , b ->
     a + b
 }

 // 或者
 val test = {a : Int , b : Int ->
     a + b
 }

 // 调用
 test(3,5) => 结果为:8

3、Lambda表达式作为函数中的参数的时候

// 源代码 
fun test(a : Int , b : Int) : Int{
     return a + b 
}

fun sum(num1 : Int , num2 : Int) : Int{
     return num1 + num2 
} 

// 调用 
test(10,sum(3,5)) 
// 结果为:18

// lambda 
fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{
     return a + b.invoke(3,5)
 } 

// 调用 
test(10,{ num1: Int, num2: Int -> 
    num1 + num2     
}) 

// 结果为:18

七、Kotlin的使用

1、创建一个Activity默认是私有的,如果这个activity可以被继承需要加open修饰

2、Java中的继承 extends 和实现 implements 在Kotlin中都可以用 :替换,多个implements之间添加 ,

open class KotlinTestActivity : BaseActivity(), View.OnClickListener

3、在布局中的View可以在activity直接调用,Kotlin默认给实现findViewById

<TextView
        android:id="@+id/tv_age"
        android:layout_width="100dp"
        android:layout_height="40dp"
        android:layout_margin="20dp"/>

tv_age?.setColor(R.color.color_4)

查看Kotlin转换成java后的代码

public View _$_findCachedViewById(int var1) {
   if (this._$_findViewCache == null) {
      this._$_findViewCache = new HashMap();
   }

   View var2 = (View)this._$_findViewCache.get(var1);
   if (var2 == null) {
      var2 = this.findViewById(var1);
      this._$_findViewCache.put(var1, var2);
   }
   return var2;
}

var10000 = (TextView)this._$_findCachedViewById(id.tv_age);
if (var10000 != null) {
   KotlinExtensionKt.setColor(var10000, 500082);
}

4、Kotlin中的多个数据可以拼接成一个String用连接,如果是一个对象中的数据要加{} 包起来

var num: Int = 10
var user1 = UserData("name1”)
println("num = $num")
println("num = ${user1.age}")

5、设置监听事件

系统的OnClickListener, OnTouchListener等属于SAM 构造可以使用Lambda替换,具体分析:

https://blog.csdn.net/blovecat/article/details/103767059

tv_avatar?.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
    }
})

// 当lambda表达式是函数调用的最后一个实参,它可以放到括号的外边。
tv_avatar?.setOnClickListener() {
}

// 当lambda表达式是函数唯一实参时,还可以去掉代码中的空括号对
tv_avatar?.setOnClickListener {
}

// 当lambda表达式只有一个参数,那么在调用该lambda表达式时,可以不指定它的参数名字,在lambda函数体内用it来代表这个参数.
tv_avatar?.setOnClickListener {
    it.alpha = 1f
}

// 当lambda表达式有多个参数,那么在调用该lambda表达式时,必须指定每一个参数的名字,如果某个参数用不到可以用 _ 来代替
tv_avatar?.setOnTouchListener { _, event ->
    if (event.action == MotionEvent.ACTION_DOWN) {

    }
    false
}

5、自定义View 三个构造方法可以写成一个

Java自定义View

public class TestView extends View {
    public TestView(Context context) {
        this(context,null);
    }

    public TestView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

}

6、Kotlin自定义View

class TestView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
    : View(context, attrs, defStyleAttr) {

    // 定义接口回调方式
    var testListener: TestListener? = null

    // Lambda 表达式回调
    var callBack = { s: String, i: Int -> Unit}

    // Lambda 表达式回调简化
    var callBack2: ((String) -> Unit)? = null

    var callBack3: ((String, Int) -> Unit)? = null

    /**
     * 系统的初始化方法
     */
    init {

    }

    fun backData() {
        callBack.invoke("back", 0)
    }

    fun backData2() {
        callBack2?.invoke("back")

        callBack3?.invoke("back", 1)
    }

    fun setCallBack(listener: TestListener?) {
        this.testListener = listener
    }

}

Kotlin接口定义:

interface TestListener {

    fun callBack1()

    /**
     * 在java中,接口中定义的方法不可以实现,实现类必须实现所有方法
     * 在Kotlin中,接口中的方法可提前进行空实现,实现类可不实现其不用的方法
     */
    fun callBack2() {}

}

Activit中调用回调函数:

var view = TestView(this)

view.setCallBack(object: TestListener{
    override fun callBack1() {

    }
})

view.callBack = { _: String, _: Int ->

}

view.callBack2 = {

}

7、单例模式

/**
* @Author: zs
* @Date: 20/12/23 上午8:29
* @Description:
*/
class InstanceKotlin {
    companion object{
        @Volatile
        private var mUtil: InstanceKotlin? = null

        /**
         * 两次判空实现单例
         * @return
         */
        val instance1: InstanceKotlin?
            get() {
                if (mUtil == null) {
                    synchronized(InstanceKotlin::class.java) {
                        if (mUtil == null) {
                            mUtil = InstanceKotlin()
                        }
                    }
                }
                return mUtil
            }

        /**
         * 静态内部类实现单例
         * @return
         */
        val instance2: InstanceKotlin
            get() = TestHolder.instance

        private object TestHolder {
            val instance: InstanceKotlin = InstanceKotlin()
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容