Kotlin关键字和注解

1、关键字:

Kotlin关键字一览表

关键字与操作符 (官)

https://blog.csdn.net/dota_wy/article/details/76060078 (荐)

?的含义

在kotlin中单独使用?表示可为空;

kotlin代码书写格式:

var result = str?.length

java代码书写格式:

if(str == null)
    result = null;          // 这里result为一个引用类型
else
    result = str.length;    // 这里result为Int

有人疑问加“?”和不加的根本区别在哪?就在于程序运行过程中对变量的赋值,如果给没有加“?”的变量赋值了null,程序就会异常。

?:的含义

在kotlin中表示三元操作符(即三目运算符)

var str : String? = null
var result = str?.length ?: -1
//等价于
var result : Int = if(str != null) str.length else -1
:(冒号)

类型和超类型之间的冒号前要有一个空格,而实例和类型之间的冒号前不要有空格:

interface Foo<out T : Any> : Bar {
    fun foo(a: Int): T
}

Kotlin 可以有包级函数,因此我们并不需要声明一个类来包装 main 函数:

fun main(args: Array<String>){
    ...
}
::(双冒号)

双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。

https://blog.csdn.net/lv_fq/article/details/72869124

/**
 * @param p1 参数1
 * @param p2 参数2
 * @param method 方法名称
 */
fun lock(p1: String, p2: String, method: (str1: String, str2: String) -> String): String {
    return method(p1, p2)
}
::class

如何获得 class 的实例

Java 当中:

public class Hello{
    ...
}

...

Class<?> clazz = Hello.class;

Hello hello = new Hello();
Class<?> clazz2 = hello.getClass();

Kotlin当中:

class Hello

val clazz = Hello::class.java

val hello = Hello()
val clazz2 = hello.javaClass

同样效果的 Kotlin 代码看上去确实很奇怪,实际上 Hello::class 拿到的是 Kotlin 的 KClass,这个是 Kotlin 的类型;
如果想要拿到 Java 的 Class 实例,那么就需要Hello::class.java。

!!(双叹号)

意味着「有一个潜在未处理的 KotlinNullPointerException 在这里」

在kotlin中表示一定不能为空;

var result : Int = str!!.length

https://zhuanlan.zhihu.com/p/27285806

abstract :

抽象类 一个类或一些成员可能被声明成 abstract 。一个抽象方法在它的类中没有实现方法。
记住我们不用给一个抽象类或函数添加 open 注解,它默认是带着的。

open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}
any:

相当于Java中的Object。

by

类委托、属性委托

companion object 伴生对象:

类似static。

在 kotlin 中,不像 java 或者 C# ,它没有静态方法。在大多数情形下,我们建议只用包级别的函数。

如果你要写一个没有实例类就可以调用的方法,但需要访问到类内部(比如说一个工厂方法),你可以把它写成它所在类的一个成员(you can write it as a member of an object declaration inside that class)更高效的方法是,你可以在你的类中声明一个伴随对象,这样你就可以像 java/c# 那样把它当做静态方法调用,只需要它的类名做一个识别就好了。

实例:获取全局的Application实例

companion object {
        lateinit var instance: App
    }
const:

使用 const 修饰符标记为 编译期常量

constructor 用于标识构造函数
class Customer(name: String){
    init {
        logger,info("Customer initialized with value ${name}")
    }
}

二级构造函数

class Person { 
    constructor(parent: Person) {
        parent.children.add(this) 
    }
}    

https://blog.csdn.net/dota_wy/article/details/76060078

3.1、注:kotlin中的类定义同时也是构造函数,这个时候是不能进行操作的,所以kotlin增加了一个新的关键字init用来处理类的初始化问题,init模块中的内容可以直接使用构造函数的参数。

class Person(name:String,age:int){
    init{
        //do some thiing
    }
}

3.2、java中加入final为不可继承,而kotlin中定义类默认前面带有修饰符final,所以,如果想继承该类,在最前面加上open或者abstract即可。即:

open class Person(name : String, age : int) {
    init{  
        // do some thing
    } 
}

3.3、如果init中没有操作,则可以省略

class Person(name : String, age : int)

3.4、如果连参数也没有,甚至可以这么写

class Person

3.5、但是当构造参数中的参数、类型变化时可能需要不只是一个构造函数,需要多组构造函数来处理不同view上的数据时,使用constructor加上参数,后面用this加上主构造函数的参数。
次级构造函数

class Person(name:String){
    var a = 1
    init{
        log("This is --> primary constructor,a = $a,name = $name")
    }
    constructor(name:String,age:Int): this(name){
        log("This is --> secondry constructor,a = ${++a}, age = $age")
    }
}
data class:

kotlin中的data class

data class就是一个类中只包含一些数据字段。

编译器在背后默默给我们生成了如下的东西

  • equals()/hashCode()
  • toString()方法
  • componentN()方法《实现解构申明》
  • copy()方法

如何申明一个简单的数据类? 有一下几点要求:

  • 主构造函数必须要至少有一个参数(就代表了必须要有一个数据字段,hashCode,equals,toString都是要依据主构造函数来生成的)

  • 主构造函数中的所有参数必须被标记为val或者var(相当于表明了数据字段的访问权限,这就达到了Java中我们手动生成set get的目的)

  • 数据类不能有以下修饰符:abstract,inner,open,sealed

  • data class只能实现接口(Kotlin1.1以前的规则),现在也可以继承其它类

    data class User(var id: Int, var name: String)

indices:
inline:

作用:修饰内联函数。

如果被调用的Lambda表达式或函数包含大量的执行代码,那么就不应该使用内联函数;
如果被调用的Lambda表达式或函数只包含非常简单的执行代码,就使用内联函数;

inner:

嵌套类:

如果想让类 B 能访问类 A 的成员,可以加 inner 标记:

class A {
    val a: String = "a"
    inner class B {
        val b: String = a
    }
}

访问:

val a = A().B().b//a输出为“a”
internal:

在Kotlin编程中有四种修饰词:private,protected,internal,public,默认的修饰词是public。

  • internal 声明,在同一模块中的任何地方可见。
fun main(args: Array<String>) {
        Test().test()
    }
    open  class BaseTest{
        private val a="该属性不能被子类访问"
        protected var b="该属性能被子类访问"
        internal var c="同一模块下被访问"
        var d="到处都可以被访问"
    }
    class Test:BaseTest(){
        fun  test(){
            println(b)
            println(c)
            println(d)
        }
    }
is:

相当于Java中的 instanceof

lateinit var 延迟初始化属性:

通常,那些被定义为拥有非空类型的属性,都需要在构造器中初始化.但有时候这并没有那么方便.

例如在单元测试中,属性应该通过依赖注入进行初始化, 或者通过一个 setup 方法进行初始化.在这种条件下,你不能在构造器中提供一个非空的初始化语句,但是你仍然希望在访问这个属性的时候,避免非空检查.

为了处理这种情况,你可以为这个属性加上 lateinit 修饰符

注意:lateinit修饰的变量/属性 不能 是原始数据类型。

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method() 
    }
}

这个修饰符只能够被用在类的 var 类型的可变属性定义中,不能用在构造方法中.并且属性不能有自定义的 getter 和 setter访问器.这个属性的类型必须是非空的,同样也不能为一个基本类型.
在一个延迟初始化的属性初始化前访问他,会导致一个特定异常,告诉你访问的时候值还没有初始化.

初始化前访问一个 lateinit 属性会抛出一个特定异常,该异常明确标识该属性被访问及它没有初始化的事实。

by lazy 懒属性(延迟加载)
val p: String by lazy {
    // 生成string的值
}

Kotlin的延迟初始化: lateinit var 和 by lazy

  • lateinit 只用于 var,而 lazy 只用于 val
  • lateinit var只是让编译期忽略对属性未初始化的检查,后续在哪里以及何时初始化还需要开发者自己决定。
  • by lazy真正做到了声明的同时也指定了延迟初始化时的行为,在属性被第一次被使用的时候能自动初始化。
object(用于创建单例模式):
object Resource {
    val name = "Name"
}
open 说明可以被继承 :

open 注解与java 中的 final相反:它允许别的类继承这个类。

默认情形下,kotlin 中所有的类都是 final ,用来表示他可以被继承。

所以,父类使用open声明类,不然默认final,无法继承。

修饰类: 说明可以被继承

open class Base(p: Int)

class Derived(p: Int) : Base(p)

修饰成员 : 在 kotlin 中坚持做明确的事。不像 java ,kotlin 需要把可以复写的成员都明确注解出来,并且重写它们

open class Base {
    open fun v() {}
    fun nv() {}
}

class Derived() : Base() {
    override fun v() {}
}
override :
reified:
sealed 密封类:

密封类用于代表严格的类结构,值只能是有限集合中的某中类型,不可以是任何其它类型。这就相当于一个枚举类的扩展:枚举值集合的类型是严格限制的,但每个枚举常量只有一个实例,而密封类的子类可以有包含不同状态的多个实例。
声明密封类需要在 class 前加一个 sealed 修饰符。密封类可以有子类但必须全部嵌套在密封类声明内部.

sealed class Expr {
    class Const(val number: Double) : Expr() 
    class Sum(val e1: Expr, val e2: Expr) : Expr() 
    object NotANumber : Expr()
}

注意密封类子类的扩展可以在任何地方,不必在密封类声明内部进行。
使用密封类的最主要的的好处体现在你使用 when 表达式。可以确保声明可以覆盖到所有的情形,不需要再使用 else 情形。

fun eval(expr: Expr): Double = when(expr) { 
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2) 
    NotANumber -> Double.NaN
    // the `else` clause is not required because we've covered all the cases
}
tailrec(尾递归):

作用:修身尾递归函数。与普通递归相比,编译器会对尾递归进行修改,将其优化成一个快速而高效的基于循环的版本,这样减少内存消耗。

tailrec fun test3(n: Int, total: Int = 1): Int =
    if (n == 1)
        total
    else
        test3(n - 1, total * n)
Unit 类型:

如果函数返回 Unit 类型,该返回类型应该省略:

var和val:

var是一个可变变量,这是一个可以通过重新分配来更改为另一个值的变量。这种声明变量的方式和java中声明变量的方式一样。
val是一个只读变量,这种声明变量的方式相当于java中的final变量。一个val创建的时候必须初始化,因为以后不能被改变。

typealias:

定义类型别名。

vararg :

功能:可变参数。一个函数最多只能有一个可变参数。

fun asList<T>(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts)
        result.add(t)
    return result
}

标记后,允许给函数传递可变长度的参数:

val list = asList(1, 2, 3)
when 用于判断 相当于java中的switch()语句

when分支:

  • 不再需要case关键字
  • case值后的冒号改为使用箭头(->)
  • default改为更有意义、更明确的else
  • when分支可以匹配多个值
  • when分支可以是任意表达式
  • when分支对条件表达式的类型没有任何要求
    when(color) {
            "Red" -> 0
            "Green" -> 1
            "Blue" -> {
                println("2")
                println("3")
            }}
            else -> throw IllegalArgumentException("Invalid color param value")
        }

when分支作为表达式:

fun test(){
    var score='B'
    
    val str= when(score){
        "A"->"优秀"
        "B"->"良好"
        "C"->"及格"
        else->{
            println("继续努力啊")
            "不及格"
        }
    }
}

处理范围(in…),处理类型(is Int,is Double…):

fun test1(){
    var score='B'
    
    val str= when(score){
        in 80..100->"优秀"
        "B"->"良好"
        "C"->"及格"
        is String->"属于字符串类型"
        else->{
            println("继续努力啊")
            "不及格"
        }
    }
}

2、注解:

@JvmField

https://www.kotlincn.net/docs/reference/java-to-kotlin-interop.html#%E9%9D%99%E6%80%81%E5%AD%97%E6%AE%B5

静态字段:
在命名对象或伴生对象中声明的 Kotlin 属性会在该命名对象或包含伴生对象的类中具有静态幕后字段。

通常这些字段是私有的,但可以通过以下方式之一暴露出来:

  • @JvmField 注解;( public static final 字段)
  • lateinit 修饰符;( public static 非-final 字段)
  • const 修饰符。(static 字段)

@JVMStatic

https://www.kotlincn.net/docs/reference/java-to-kotlin-interop.html#%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95

静态方法:
在 “companion object” 中的公共函数必须用使用 @JvmStatic 注解才能暴露为静态方法。

如果使用 @JvmStatic 注解,编译器既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法。
如果没有这个注解,这些函数仅可用作静态 Companion 字段上的实例方法。

3、作用域函数

作用域函数(官)

Kotlin之let,apply,with,run函数区别

Kotlin之let,apply,run,with等函数区别2

Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:let、run、with、apply 以及 also。

每个作用域函数之间有两个主要区别:

  • 引用上下文对象的方式
  • 返回值
函数名定义参数返回值extension
letfun T.let(f: (T) -> R): R = f(this)it闭包返回
applyfun T.apply(f: T.() -> Unit): T { f(); return this }无,可以使用thisthis
withfun with(receiver: T, f: T.() -> R): R = receiver.f()无,可以使用this闭包返回否,调用方式有区别
runfun T.run(f: T.() -> R): R = f()无,可以使用this闭包返回

let

默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行,或者指定return

apply:

apply函数是这样的,调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象.

apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。

run:

run函数和apply函数很像,只不过run函数是使用最后一行的返回,apply返回当前自己的对象。

run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理。

with:

with函数是一个单独的函数,并不是Kotlin中的extension,所以调用方式有点不一样,返回是最后一行,然后可以直接调用对象的方法,感觉像是let和apply的结合。

also

上下文对象作为 lambda 表达式的参数(it)来访问。 返回值是上下文对面本身。

4、定义类

4.1、属性修饰符

  • annotation //注解类
  • abstract //抽象类
  • final //类不可继承,默认属性
  • enum //枚举类
  • open //类可继承,类默认是final的

4.2、访问权限修饰符

  • private //仅在同一个文件中可见
  • protected //同一个文件中或子类可见
  • public //所有调用的地方都可见
  • internal //同一个模块中可见
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值