文章目录
- 1、关键字:
- ?的含义
- ?:的含义
- :(冒号)
- ::(双冒号)
- ::class
- !!(双叹号)
- abstract :
- any:
- by
- companion object 伴生对象:
- const:
- constructor 用于标识构造函数
- data class:
- indices:
- inline:
- inner:
- internal:
- is:
- lateinit var 延迟初始化属性:
- by lazy 懒属性(延迟加载)
- object(用于创建单例模式):
- open 说明可以被继承 :
- override :
- reified:
- sealed 密封类:
- tailrec(尾递归):
- Unit 类型:
- var和val:
- typealias:
- vararg :
- when 用于判断 相当于java中的switch()语句
- 2、注解:
- @JvmField
- @JVMStatic
- 3、作用域函数
- let
- apply:
- run:
- with:
- also
- 4、定义类
- 4.1、属性修饰符
- 4.2、访问权限修饰符
Kotlin关键字一览表
关键字与操作符 (官)
?的含义
在kotlin中单独使用?表示可为空;
kotlin代码书写格式:
java代码书写格式:
有人疑问加“?”和不加的根本区别在哪?就在于程序运行过程中对变量的赋值,如果给没有加“?”的变量赋值了null,程序就会异常。
?:的含义
在kotlin中表示三元操作符(即三目运算符)
:(冒号)
类型和超类型之间的冒号前要有一个空格,而实例和类型之间的冒号前不要有空格:
Kotlin 可以有包级函数,因此我们并不需要声明一个类来包装 main 函数:
::(双冒号)
双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。
::class
如何获得 class 的实例
Java 当中:
Kotlin当中:
同样效果的 Kotlin 代码看上去确实很奇怪,实际上 Hello::class 拿到的是 Kotlin 的 KClass,这个是 Kotlin 的类型;
如果想要拿到 Java 的 Class 实例,那么就需要Hello::class.java。
!!(双叹号)
意味着「有一个潜在未处理的 KotlinNullPointerException 在这里」
在kotlin中表示一定不能为空;
abstract :
抽象类 一个类或一些成员可能被声明成 abstract 。一个抽象方法在它的类中没有实现方法。
记住我们不用给一个抽象类或函数添加 open 注解,它默认是带着的。
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实例
const:
使用 const 修饰符标记为 编译期常量
constructor 用于标识构造函数
二级构造函数
3.1、注:kotlin中的类定义同时也是构造函数,这个时候是不能进行操作的,所以kotlin增加了一个新的关键字init用来处理类的初始化问题,init模块中的内容可以直接使用构造函数的参数。
3.2、java中加入final为不可继承,而kotlin中定义类默认前面带有修饰符final,所以,如果想继承该类,在最前面加上open或者abstract即可。即:
3.3、如果init中没有操作,则可以省略
3.4、如果连参数也没有,甚至可以这么写
3.5、但是当构造参数中的参数、类型变化时可能需要不只是一个构造函数,需要多组构造函数来处理不同view上的数据时,使用constructor加上参数,后面用this加上主构造函数的参数。
次级构造函数
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 标记:
访问:
internal:
在Kotlin编程中有四种修饰词:private,protected,internal,public,默认的修饰词是public。
- internal 声明,在同一模块中的任何地方可见。
is:
相当于Java中的 instanceof
lateinit var 延迟初始化属性:
通常,那些被定义为拥有非空类型的属性,都需要在构造器中初始化.但有时候这并没有那么方便.
例如在单元测试中,属性应该通过依赖注入进行初始化, 或者通过一个 setup 方法进行初始化.在这种条件下,你不能在构造器中提供一个非空的初始化语句,但是你仍然希望在访问这个属性的时候,避免非空检查.
为了处理这种情况,你可以为这个属性加上 lateinit 修饰符
注意:lateinit修饰的变量/属性 不能 是原始数据类型。
这个修饰符只能够被用在类的 var 类型的可变属性定义中,不能用在构造方法中.并且属性不能有自定义的 getter 和 setter访问器.这个属性的类型必须是非空的,同样也不能为一个基本类型.
在一个延迟初始化的属性初始化前访问他,会导致一个特定异常,告诉你访问的时候值还没有初始化.
初始化前访问一个 lateinit 属性会抛出一个特定异常,该异常明确标识该属性被访问及它没有初始化的事实。
by lazy 懒属性(延迟加载)
Kotlin的延迟初始化: lateinit var 和 by lazy
- lateinit 只用于 var,而 lazy 只用于 val
- lateinit var只是让编译期忽略对属性未初始化的检查,后续在哪里以及何时初始化还需要开发者自己决定。
- by lazy真正做到了声明的同时也指定了延迟初始化时的行为,在属性被第一次被使用的时候能自动初始化。
object(用于创建单例模式):
open 说明可以被继承 :
open 注解与java 中的 final相反:它允许别的类继承这个类。
默认情形下,kotlin 中所有的类都是 final ,用来表示他可以被继承。
所以,父类使用open声明类,不然默认final,无法继承。
修饰类: 说明可以被继承
修饰成员 : 在 kotlin 中坚持做明确的事。不像 java ,kotlin 需要把可以复写的成员都明确注解出来,并且重写它们
override :
reified:
sealed 密封类:
密封类用于代表严格的类结构,值只能是有限集合中的某中类型,不可以是任何其它类型。这就相当于一个枚举类的扩展:枚举值集合的类型是严格限制的,但每个枚举常量只有一个实例,而密封类的子类可以有包含不同状态的多个实例。
声明密封类需要在 class 前加一个 sealed 修饰符。密封类可以有子类但必须全部嵌套在密封类声明内部.
注意密封类子类的扩展可以在任何地方,不必在密封类声明内部进行。
使用密封类的最主要的的好处体现在你使用 when 表达式。可以确保声明可以覆盖到所有的情形,不需要再使用 else 情形。
tailrec(尾递归):
作用:修身尾递归函数。与普通递归相比,编译器会对尾递归进行修改,将其优化成一个快速而高效的基于循环的版本,这样减少内存消耗。
Unit 类型:
如果函数返回 Unit 类型,该返回类型应该省略:
var和val:
var是一个可变变量,这是一个可以通过重新分配来更改为另一个值的变量。这种声明变量的方式和java中声明变量的方式一样。
val是一个只读变量,这种声明变量的方式相当于java中的final变量。一个val创建的时候必须初始化,因为以后不能被改变。
typealias:
定义类型别名。
vararg :
功能:可变参数。一个函数最多只能有一个可变参数。
标记后,允许给函数传递可变长度的参数:
when 用于判断 相当于java中的switch()语句
when分支:
- 不再需要case关键字
- case值后的冒号改为使用箭头(->)
- default改为更有意义、更明确的else
- when分支可以匹配多个值
- when分支可以是任意表达式
- when分支对条件表达式的类型没有任何要求
when分支作为表达式:
处理范围(in…),处理类型(is Int,is Double…):
@JvmField
静态字段:
在命名对象或伴生对象中声明的 Kotlin 属性会在该命名对象或包含伴生对象的类中具有静态幕后字段。
通常这些字段是私有的,但可以通过以下方式之一暴露出来:
- @JvmField 注解;( public static final 字段)
- lateinit 修饰符;( public static 非-final 字段)
- const 修饰符。(static 字段)
@JVMStatic
静态方法:
在 “companion object” 中的公共函数必须用使用 @JvmStatic 注解才能暴露为静态方法。
如果使用 @JvmStatic 注解,编译器既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法。
如果没有这个注解,这些函数仅可用作静态 Companion 字段上的实例方法。
作用域函数(官)
Kotlin之let,apply,with,run函数区别
Kotlin之let,apply,run,with等函数区别2
Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:let、run、with、apply 以及 also。
每个作用域函数之间有两个主要区别:
- 引用上下文对象的方式
- 返回值
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.1、属性修饰符
- annotation //注解类
- abstract //抽象类
- final //类不可继承,默认属性
- enum //枚举类
- open //类可继承,类默认是final的
4.2、访问权限修饰符
- private //仅在同一个文件中可见
- protected //同一个文件中或子类可见
- public //所有调用的地方都可见
- internal //同一个模块中可见