Kotlin学习

本文档详细介绍了Kotlin语言的基础配置及使用技巧,包括环境搭建、泛型应用、修饰符理解、属性与函数详解等内容,特别关注了Kotlin与Java代码互操作时的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开始学习Kotlin,将学习过程中遇到的,看到的知识点记录下来。

配置Kotlin环境

》在项目根目录build.gradle配置

buildscript {
    ext.kotlin_version = '1.2.71'

    repositories {
        maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

》然后在app 的build.gradle中

apply plugin: 'kotlin-android'

还需要在依赖中

compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"//不做检测会出现问题,找不到库

这时,就可以使用Kotlin开发了,而且可实现java,kotlin互调

》如果想简化findViewById过程,可添加

apply plugin: 'kotlin-android-extensions'

》如果Kotlin代码里面有使用到注解(@JvmStatic,@JvmField等),可添加

apply plugin: 'kotlin-kapt'

》》》构造函数

》》》泛型

在 Kotlin 中使用泛型,其中引入了 in 和 out

Out (协变)

如果你的类是将泛型作为内部方法的返回,那么可以用 out:

interface Product<out T> {
    fun produce(): T
}

In(逆变)

如果你的类是将泛型对象作为函数的参数,那么可以用 in:

interface IPresenter<in V: IBaseView> {

    fun attachView(mRootView: V)

    fun detachView()

}

Invariant(不变)

如果既将泛型作为函数参数,又将泛型作为函数的输出,那就既不用 in 或 out。

》》》》修饰符

在Kotlin中,所有的类默认都是final的。如果需要允许类可以被继承,那么需要使用open声明。

在Kotlin中,abstract的用法几乎和java一致。当使用abstract修饰符时,可以忽略open,因为被abstract修饰的类默认具有open属性。

对于接口类来说,基本上用不上final,open,abstract,因为接口类默认是open,且不能被声明为final,如果接口类的方法没有函数体,那么其为abstract,但是不需要明确指出。

》》》Kotlin中属性的set,get

语法:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

格式就如上所示, set和get可写也可不写, 不写的话会有默认的实现, 需要注意的是val修饰的变量是没有set方法的, 也不允许重写set方法。

var no : Int = 0
        get() = field
        set(value) {
            if (value < 10) {
                field = value
            } else {
                field = -1
            }
        }

field的用法, field被大神们翻译成Backing Fields(后端变量), 它的作用就类似于java里的this.属性名。但是不能直接使用this.属性名,会造成递归调用内存溢出的,  因为在set和get中是不允许有本身的局部变量的 (例如如果属性名是name, 在set/get里是不能有这个变量的),  因为属性的调用也涉及到了set/get会造成递归调用, 所以要解决引用自身的问题, kotlin发明了field(后端变量)来解决这个问题。

注:不是set/get里不允许有局部变量, 是不允许有和属性本身相同名字的局部变量。

一、

Java中,所有的代码都依托于类而存在。但是在有些情况下,我们发现有些地方可能不是属于某一个特定类,有些属性也不是属于某一个特定类。所以我们就创建了很多的Java工具类 和 属性的常量类。如:

Java代码

public class Constant{

    public static final String BASE_URL = "http://baobab.kaiyanapp.com/api/";

}
public class AppUtils{

    public static String dateToString(Data date){

        //...
        return "";
    }
}

其实上面的类只是为了承载我们的静态属性 和 方法,作为了静态方法  和 属性的容器,这就是我们目前遇到的问题,一大堆无用的容器类。

那么在Kotlin中,如何解决?

在Java中,类处于顶层,类包含属性 和 方法。在Kotlin中,函数站在了类的位置,我们可以直接把函数放在代码文件的顶层,让它不属于任何类。

Kotlin代码

package com.hazz.kotlinmvp.util

fun dateToString(date: Date): String{

    //...
    return ""

}

调用上面方法

import com.hazz.kotlinmvp.util

fun main(args: Array<String>){
    dateToString(Date())
}

除了可以把函数直接放在文件顶层,还可以把属性直接放在顶层。

package com.seven.config

val REQUEST_URL = "http://localhost:8080/"

二、

在顶层声明的时候

1.变量定义

立即初始化

var num: Int = 10

推导出类型

var num = 5

没有初始化的时候,必须声明类型

var num : Float      (这里的Float不能省略)

2.

val 用于声明不可变变量,也就是表示可读但不可写,相当于final修饰的变量

var 用于声明可变变量,可读可写

在类中声明 以及 声明可空变量

上面讲述的是变量的基础定义,而且只有在顶层声明的情况下 可以不用实例化。但是在实际开发中,一般都是在一个类中去定义变量,这种情况被成为声明类的属性。

特点如下:必须初始化,如果不初始化,需要使用 lateinit 关键字.

后期初始化 和 延迟初始化

1)后期初始化

声明后期初始化属性的特点:

》使用 lateinit 关键字

》必须是可读且可写的变量,即用 var 声明的变量

》不能声明于可空变量

》不能声明于基本数据类型

》声明后,在使用该变量前必须赋值,不然会抛出UninitializedPropertyAccessException异常

如:

        lateinit var a: Int    //会报错,因为是基本数据类型

       声明组件: lateinit var mTextView: TextView

      赋值:         mTextView = findViewById(R.id.text)

2)延迟初始化

所谓延迟初始化:是指当程序在第一次使用到这个变量(属性)的时候再初始化。

声明延迟初始化属性的特点:

     使用 lazy{} 高阶函数,不能用于类型推断,且该函数在变量的数据类型后面,用 by 连接。

     必须是只读变量,即用 val 声明的变量。

 如:  private val mStr: String by lazy { "延迟初始化。。。 "}

声明常量

Kotlin中只用 val 修饰还不是常量,只是一个不能修改的变量,还需要加上const。

特点: const只能修饰 val,不能修饰 var。

声明常量三种方式:

1)在顶层声明

2)在 object 修饰的类中声明,在Koltin中称为对象声明,相当于java中的单例类

3)在伴生对象中声明

object Constants {

    val REQUEST_BASE_URL = "http://v.juhe.cn/"

    val KEY = "1be865c0e67e3"
}

》》》》@JvmStatic   @JvmField

如果纯用Kotlin编码,这两个注解没什么用处。这两个注解的用处都是在 Kotlin 与 Java代码互操作时。

@JvmStatic只能用在object类中 或者 companion类中。

通过java代码进行调用时,通过@JvmStatic声明的变量或者函数可以直接使用,未声明的变量或者函数不能直接调用。

在《Android Kotlin 指南》的文档中有提到:

伴生函数:

在"companion object"中的公共函数必须使用@JvmStatic 注解 才能暴露为静态方法。
如果没有这个注解,这些函数仅可用作静态Companion字段上的实例方法。

伴生属性:

在companion object中的公共、非const的属性实际上为常量,必须用@JvmField注解才能暴露为静态字段。
如果没有这个注解,这些属性只能作为静态Companion字段中奇怪命名的'getters'实例。而只使用@JvmStatic 
而不是@JvmField的话,会将奇怪命名的'getters'移到类的静态方法中,但仍然是不正确的。

示例:

    companion object {

        //初始化fragment
        fun instance(tagId: Int): TagListFragment {
            val fragment = TagListFragment()
            val bundle = Bundle()
            bundle.putInt(BUNDLE_KEY_TAGID, tagId)
            fragment.arguments = bundle
            return fragment
        }
    }

在java中代码中调用

fragment = TagListFragment.Companion.instance(tagId);

如果给instance方法加上@JvmStatic注解,那么在java中调用(这时候调用的方式跟Java调用静态方法一致)

fragment = TagListFragment.instance(tagId);

》》》函数

1.with

定义:fun <T,R> with(receiver:T, block:T.() -> R):R

功能:将对象作为函数的参数,在函数内可通过this指代该对象。返回值为函数的最后一行 或 return表达式。

实例:

var paint = Paint()
paint.color = Color.BLACK
paint.strokeWidth = 1.0f
paint.textSize = 18.0f
paint.isAntiAlias = true

改为使用with:

var paint = Paint()
with(paint) {
     color = Color.BLACK
     strokeWidth = 1.0f
     textSize = 18.0f
     isAntiAlias = true
}

仅在不可空的对象上,且不需要返回值时使用。

2.takeIf 和 takeUnless

takeIf

定义:fun T.takeIf(predicate:(T) -> Boolean):T?

功能:传递一个函数参数,如果函数结果为true,返回T对象,否则返回null.

takeUnless

定义:fun T.takeUnless(predicate:(T) -> Boolean):T?

功能:与takeIf相反,参数函数返回false时返回T对象,否则返回null。

3. run

定义:1》fun run(block:() -> R):R    2》 fun <T,R> T.run(block:T.() -> R):R

功能:run函数返回值为函数体最后一行,或return表达式。

如果需要计算某个值或想要限制多个局部变量的作用域,可以使用run,如果需要将显示参数转换为隐式,请使用run。

4. repeat

定义: fun repeat(times:Int, action:(Int) -> Unit)

功能:重复执行action函数times次,times从0开始

实例:

    repeat(5){ println("count:$it") }

等价于

    for(i in 0..4) { println("count:$i")  }

5. let

定义:fun <T,R> T.let(block:(T) -> R):R

功能:调用对象(T)的let函数,则该对象为函数的参数。在函数内可以通过it指代该对象。返回值为函数的最后一行或指定return表达式。

以下几种情况适合使用let

>判断某个值不为null时,执行代码;

>将可空对象转换为另一个可空对象

>限制单个局部变量的作用域

注意:如果判断一个值非空时处理一段逻辑,但为空时也有另一段逻辑需要处理,这时候不适合用let。

6. apply

定义:fun T.apply(block:T.() -> Unit):T

功能:调用对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象。

它跟run函数的区别,run返回的是最后一行,apply返回的是对象本身。

如果代码块中没有访问对应变量的任何功能,并且还想返回相同的值,请使用apply(),最常见的用法就是初始化对象时。

7. also

定义: fun T.also(block:(T) -> Unit):T

功能:调用对象的also函数,在函数块内可以通过 it 指代该对象,并返回该对象本身。

》》》构造函数

Java的构造函数:

》多个构造函数,构造函数之间是重载的

》可以不声明构造函数,编译器会自动生成一个无参的构造函数

Kotlin中构造函数:

》可以有一个主构造函数  和 多个次构造函数

》可以只有主构造函数 和 次构造函数

》主、次构造函数同时存在的时候,次构造函数必须直接或间接的委托到主构造函数

》没有声明主构造函数,会自动生成一个无参数的主构造函数,这点与java一样

》在主构造函数中可以设置默认值

class Person public constructor(var name: String){}

》》》数据类

声明实体类只需一行代码

data class User(val id: String, var name: String = "")

属性的set/get方法,toString方法自动生成。

可以这么使用:User(name = "zhangsan", id = "111")   //可以打乱顺序

》》》

【强制】在java和kotlin混编时,kotlin中定义的能被java代码访问到的方法,其方法参数必须定义为可空类型,防止在java中调用该方法时,因为传入了null导致崩溃。除非你定义的方法中,参数如果为空需要主动抛出异常。

》》》字符串截取

因为在Kotlin中,字符串的截取的函数subString()是调用了Java中的subString()函数。

Kotlin中除了调用subString()函数外,还可以调用subSequence()函数

》》》》

Nullable变量的使用

  • “?”符号的使用
    Nullable变量进行操作时要带“?”,当变量为null时,不会出现异常,而是返回结果null:

    var name: String? = null
    var len = name?.length
    print(len == null)  //输出:true
  • “?:”符号的使用
    这个符号的作用是当它左边的结果为null时,进行右边的操作。
    左边结果不为null:

    var a: String? = "hello"
    var b = a?.length ?: 100  //很明显左边不为null
    println(b)  //输出: 5

左边结果为null:

    var a: String? = null
    var b = a?.length ?: 100  //左边为null,返回右边的100
    println(b)  //输出: 100

》》》》

字符串拼接

字符串拼接像java一样可以使用"+"后者使用字符串模板

val s = "abc" + 1

val i = 10
println("i = $i") // 输出“i = 10”

//用花括号括起来的任意表达式
val s = "abc"
println("$s.length is ${s.length}") // 输出“abc.length is 3”

》》》》》》》》》》》》》》》》》位运算

shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与  &
or(bits) – 或   ||
xor(bits) – 异或
inv() – 反向

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值