一、基础语法
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
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()
}
}
}