kotlin-筑基(1)

1. val、var、const

var、val 声明的变量分为三种类型:顶层属性、类属性和局部变量;

var 属性可以生成 getter 和 setter,是可变的(mutable),val 属性只有 getter,是只读的(read-only,注意不是 immutable);

局部变量只是一个普通变量,不会有 getter 和 setter,它的 val 等价于 Java 的 final,在编译时做检查。

const 只能修饰没有自定义 getter 的 val 属性,而且它的值必须在编译时确定。

val 没有 setter,是只读但非不可变

var 可变

const 只读不可变,只存在顶层或 object 中

Java 中可以使用 static final 来定义常量,这个常量会存放于全局常量区,这样编译器会针对这些变量做一些优化,例如,有三个字符串常量,他们的值是一样的,那么就可以让这个三个变量指向同一块空间。

局部变量无法声明为 static final,因为局部变量会存放在栈区,它会随着调用的结束而销毁。

kotlin引入关键字 const 来定义常量,但是这个常量跟 Java 的 static final 是有所区别的,如果它的值无法在编译时确定,则编译不过,因此 const 所定义的常量叫编译时常量


2.什么是幕后字段

2.1 kotlin的普通属性

在kotlin中,声明一个属性涉及到2个关键字:var 和 val

  • var 声明一个可变属性
  • val 声明一个只读属性

在kotlin中 gettter 和 setter 跟Java的getXX 和 setXX 方法一样,叫做访问器。读一个属性的本质是执行了读访问器getter,类似的,写一个属性的实质就是执行了属性的写访问器setter。

2.2 幕后字段

在 getter 和 setter 中用 field 来表示幕后字段,重写 setter 和 getter ,没有使用 field 不会有幕后字段,有幕后字段的属性转换成Java代码一定有一个对应的Java变量

class Bean{
    val gender:Int?=null
    
    // field:幕后字段的实际使用场景
    var name = ""
       set(value) {
            field= if (value==gender.toString()) "男" else "女"
        }
        get() {
            field = if (gender==1) "男" else "女"
            return field
        }
     
    // 重写 setter 和 getter 并且未显示使用 field 是没有幕后字段的
    var nameCopy :Int
        set(value) {
        }
        get() {
           return 1
        }
        
    // 另外一种 val 的写法 ,没有幕后字段   
	val name:String
        get() {
            return if (gender==1) "男" else "女"
        }
}

3. for 循环

// 1. until:左闭右开
for (i in 0 until 100) {
    println(i) // 0 ~ 99
}

// 2. ..:全闭区间(补充:可以通过 val test = 0..100 这种语句声明一个区间
for (i in 0..100) {
    println(i) // 0 ~ 100
}

// 3. downTo:降序的全闭区间
for (i in 100 downTo 0){
    println(i) // 100 ~ 0
}



4. init{}

class Init{
    companion object{
       	// 相当于 java 中的 static {}
        init{
            
        }
    }
    
    // 相当于 java 的构造函数,主构造函数,没有声明则是默认的无参构造函数
    init{
        
    }
}

5. data class

查看kotlin字节码反编译java文件后

  1. data class 的类已经重写了hashCode() 和 equals() 方法
  2. 普通class 是不会的

5.1 gson解析

下面例子用到 @SerializedName 标记后台字段,解析时候就能成功解析。

data class Goods(
	val name:String,
    @SerializedName("price")
    var _price:Int
)

5.2 重写setter/getter

由于data class 是 final 的,并且其 setter/getter也是final的,所以无法重写,要想实现重写,则需要将属性定义到类中。

data class Person(val name:String){
    var age:Int=0
    	set(value){
            field =value
        }
    	get()=field
}

//使用的时候
fun call(){
    val p = Person("monk")
    p.age = 10
}

6. 内联函数

6.1 inline

让变量内联用的是const ,而让函数内联则用 inline

可以在某些情况下提升效率:

  1. 在编译时期,把调用这个函数的地方用这个函数的方法体进行替换(复制和 铺平);
  2. inline 适合在包含 lambda 参数的函数上,是因为 lambda 参数多出来的类会增加内存的分配。
// kotlin 
inline fun setCallback(listener: () -> Unit) {
    listener.invoke()
}

fun testInline(){
    setCallback { 
        print("测试 inline ")
    }
}

/**===============================java=====================================*/
 public static final void setCallback(@NotNull Function0 listener) {
      listener.invoke();
 }

 // 如果加了inline,方法调用栈就少了一层
public static final void testInline() {
    String var2 = "测试 inline ";
    System.out.print(var2);
}

// 如果不加inline,反编译后的java函数如下,会产生Function0实例
public static final void testInline(){
    setCallback((Function0)null.INSTANCE);
}

6.2 return

inline 函数支持 return,没加 inline 的函数在被调用时,要使用return ,一般会 return@方法名 方式,并且一般方法的后续代码还是会执行的。inline函数则不会让后续代码执行(想想代码平铺就理解了)。

Lambda表达式里不允许使用return,除非—— 这个Lambda是内联函数的参数。

inline fun hello(block:()->Unit){
    println("inline start")
    block()
    println("inline end")
}

fun call(){
    hello{
        println("block start")
        return
    }
    println("block end ")
}

fun main() {
    // 结果:inline start  -> block start
    call()
}

6.3 noinline

内联函数的「函数参数」 不允许作为参数传递给非内联的函数(看起来跟java 的 static 有点像,静态方法不能调用非静态方法)。

也就是说如果希望只内联一部分传给内联函数的 lambda 表达式参数,那么可以用 noinline 修饰符标记不希望内联的函数参数。

// 编译器提示这里的 inline 对性能预期影响是微不足道的(无需inline,但这里为了说明noinline的用法)
inline fun hello(noinline block:()->Unit){
    println("inline start")
    // 如果这里没有用 noinline 修饰 block,此处会报错,
    call(block) 
    println("inline end")
}

// 非 inline 函数,
fun call(block:()->Unit){
    println("call start")
    block()
    println("call end")
}

在其他函数调用时候,noinline 也会其作用:

inline fun hello(preAction:()->Unit, noinline postAction:()->Unit){
    preAction()//做点前置工作
    println("Hello!")
    postAction()//做点后置工作
    
    //如果没有oninline修饰,这里编译器会报错
    return postAction
}

6.4 crossinline

什么时候需要crossinline?当你需要突破内联函数的「不能间接调用参数」的限制的时候。但其实和noinline一样,你并不需要亲自去判断,只要在看到Android Studio给你报错的时候把它加上就行了。

inline fun hello(crossinline postAction:()->Unit){
    println("hello!")
    runOnUiThread{
        //不加crossinline编译器会报错
        postAction()
        //return 这里不再允许使用return
    }
}

Kotlin增加了一条额外规定:内联函数里被crossinline修饰的函数类型的参数,将不再享有「Lambda表达式可以使用return」的福利。所以这个return并不会面临「要结束谁」的问题,而是直接就不允许这么写。


7. sealed class

sealed class,密封类。具备最重要的一个特点:

其子类可以出现在定义 sealed class 的不同文件中,但不允许出现在与不同的 module 中,且需要保证 package 一致。这样既可以避免 sealed class 文件过于庞大,又可以确保第三方库无法扩展你定义的 sealed class,达到限制类的扩展目的。

是一个有特定数量子类的类,看上去和枚举有点类似。

 // TestSealed.kt
 sealed class GameAction(times: Int) {
     // Inner of Sealed Class
     object Start : GameAction(1)
     data class AutoTick(val time: Int) : GameAction(2)
     class Exit : GameAction(3)
 }

fun main(args:Array<String>){
    var sc:GameAction = GameAction.Start()
}

8. object

类和对象同时创建,相当于 懒汉式单例,里面的方法是 final 方法

  1. val 变量相当于 private static final
  2. var 变量相当于 private static

kotlin中有四种方式表示静态:

  1. object单例类模式
  2. companion object
  3. JvmStatic注解模式
  4. 顶层函数模式

object:反编译java代码是由一个静态类包含一系列非静态函数

companion object:同上

JvmStatic:只能注解在单例或companion object 中的方法上,反编译java代码是一个静态函数

顶层函数:反编译java代码是一个静态函数


9. open

在java中允许创建任意的子类并重写任意的方法,除非显示的使用了final关键字进行阻止。

而在kotlin的世界里面则不是这样,在kotlin中它所有的类默认都是 final的,那么就意味着不能被继承,而且在类中所有的方法也是默认是final的,那么就是kotlin的方法默认也不能被重写,所以open 关键字就是用来解决这个现象的。


10. inner

kotlin 默认内部类是 static

  1. inner 会让内部类成为非静态,这样可以使用外部类方法和函数

  2. 因为外部类方法或函数有非静态,而 java 是不允许静态使用非静态(kotlin也是一样)

静态属于类,非静态属于对象,也就是静态调用非静态可能会出现对象未初始化的情况,因此编译不通过

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值