由于kotlin优秀的类型推导机制,在定义变量时无需声明其类型,只需根据场景使用var和val进行定义即可
类似于java中加了final
的修饰的变量,其值初始化后不可改变
如下定义了一个不可变的整形变量
val value = 10
便于代码可读性,我们可以显式声明其类型
val value : Int = 10
类似于java中非final
修饰的变量
如下定义了一个可变的整形变量
var value = 10
value = value - 5
格式
fun 函数名称(参数1:参数类型,参数2:参数类型):返回类型{}
以下为计算某个字符串的长度,fun
为kotlin内定义函数的关键字;若不需要返回值,直接省略
:返回类型
即可,则对应java中void
关键字修饰的方法
fun example(value: String) : Int{
if (value != null){
return value.length
}
return 0
}
以比较两数大小作为例子,具体如下所示:
示例一:传入两个整形参数,返回类型为Int,通过if
进行比较,返回较大值
fun JudgeMax(paramA: Int , paramB: Int) : Int{
var value = 0
if (paramA > paramB)
value = paramA
else
value = paramB
return value
}
示例二:因为在kotlin中if是带返回值的,所以根据示例一,可简写为如下;当只有一句表达式时,可省略{}
/**
* fun:方法/函数定义关键字
* JudgeMaxA:方法/函数名称
* 括号内:若需要形参,则:参数名称:参数类型;多个参数中间用逗号隔开,若不需要参数,空括号即可
* 返回类型:Int*/
fun JudgeMaxA(paramA: Int , paramB: Int) : Int{
val value = if (paramA > paramB)
paramA
else
paramB
return value
}
示例三:根据示例二继续简写,直接通过比较返回,无需额外建立一个局部变量
/**
* 因为在kotlin中if具有返回值的功能,所以可简写为如下*/
fun JudgeMaxB(paramA: Int , paramB: Int) : Int{
return if (paramA > paramB)
paramA
else
paramB
}
示例四:当函数只有一行代码时,无需编写函数体,直接使用=
号连接函数即可,在由于kotlin自动类型推导功能,直接在函数之后进行if判断
/**
* 由于if具有返回值功能,加上kotlin自动类型推导功能,可以使用=号进行连接,并且因为传入型参为int型,所以函数返回类型也可省略*/
fun JudgeMaxC(paramA: Int , paramB: Int) = if (paramA > paramB)
paramA
else
paramB
示例五:使用内置函数max,其返回值为int型,故与C例同理
/**
* 使用内置函数max,其返回值为int型,故与C例同理*/
fun JudgeMaxD(paramA : Int , paramB : Int) = max(paramA,paramB)
kotlin
中的when
与java
中的switch
类似,但switch
只支持整形、短整形,在1.8之后支持字符串匹配,但when
支持任何类型匹配
示例一:when同样带返回值,当执行逻辑只有一个表达式时,可省略{}函数体
;其中else
等同于java中的default
关键字,当前面没有匹配项时,执行else
/**
* when函数类似于java中的switch函数
* when函数允许传入任意类型参数
* 其中else对应switch的default
* 格式:匹配值 -> {执行逻辑}
* 当执行逻辑只有一句话时,包裹的{}可省略*/
fun getName(id : String) = when (id){
"111" -> {"Tom"}
"222" -> "Jack"
"333" -> "Peter"
else -> "NULL"
}
示例二:Number是一个内置的抽象类,Int,Double,Long…都是其子类
is
为内置关键字,类似于java中的instance
;通过判断传入参数类型,打印相对于的类型
/**
* Number是一个内置的抽象类,Int,Double,Long...都是其子类
* is为内置关键字,类似于java中的instance*/
fun getType(num : Number){
when(num){
is Int -> Log.d("type","type = Int")
is Double -> Log.d("type","type = Double")
else -> Log.d("type","type = null")
}
}
在kotlin
中舍弃了for-i
,引进了for-in
,类似于加强版for-each
示例一:区间为kotlin的特点之一,..
为双端区间写法
/**
* .. 双端区间,等同于数学中的闭区间[0,10]
* 区间左端必须小于等于右边
* for-in为java中for-each加强版
* 因为kotlin的类型推导机制,循环变量i不需要定义类型*/
fun testForInA(){
val range = 0 .. 10
for ( i in range){
Log.d(TAG, "current num = $i")
}
}
示例二:因为数组下标是从0开始,所以在遍历数组时,数组长度为n的数组,当下标移动到n即越界,所以使用until
关键字,代表左开右闭区间
/**
* until 左开右闭区间,等同于数学中的闭区间[0,10)
* 区间左端必须小于等于右边
* for-in为java中for-each加强版
* 因为kotlin的类型推导机制,循环变量i不需要定义类型*/
fun testForInB(){
val range = 0 until 10
}
示例三:当我们只需要一个数字内的偶数时,可使用关键字step
,控制下标移动步长
/**
* step : 等同于i+=2*/
fun testForInC(){
val range = 0 until 10
// val range = 0 until 10 step 2
for (i in range step 2){
Log.d(TAG, "current num = $i")
}
}
示例四:以上..
和until
两种区间关键字都是代表着升序,即左边值必须小于等于右边的值,当我们需要体现降序时,可使用关键字downTo
/**
* ..和until都是左边小于等于右边,即为升序
* 若需使用降序,则使用downTo关键字,数学表达式为:[10,0]*/
fun testForInD(){
val range = 10 downTo 0
for (i in range){
Log.d(TAG, "current num = $i")
}
}
在kotlin
中非抽象类不可被继承,如需此类允许被外界子类继承,则需在类名前方加上open
关键字,代表此类允许被继承
示例:
open class PersonUtil{
//do something ...
}
子类继承如下,使用:
代替java中的extends
关键字,即实现接口也使用:
,代替implements
class StudentUtil : PersonUtil() {
}
在kotlin中有主构造函数
和次构造函数
之分,只允许有一个主构造函数,可以拥有多个此构造函数,两者区别在于,主构造函数没有函数体,直接在类名之后完成定义
open class PersonUtil(name: String , age: Int) {
}
若如需在主构造函数内执行一些逻辑,可在init
函数内完成
init {
//...
}
次构造函数使用constructor
关键字声明,其中次构造函数必须调用主构造函数
/**
* 所有的次构造函数必须调用主构造函数*/
constructor(name: String,age: Int,sex: String) : this(name,age) {
//...
}
/**
* 通过调用第一个次构造函数,间接调用主构造函数*/
constructor() : this("",0,""){
//...
}
修饰符 | Java | Kotlin |
---|---|---|
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
protected | 当前类、子类、同包路径下可见 | 当前类、子类可见 |
default | 同包路径下可见(默认) | 无 |
internal | 无 | 同一模块中的类可见 |
在类名前方加上data
关键字
/**
* data:即为数据类,系统自动重新toString(),hashCode(),equal()等方法,减少不必要代码量*/
data class BeanUtil(val value : String)
在java中需要私有化构造方法,防止外部new新对象,还需要synchronized
和双重判空去优化单例获取,但在kotlin中只需要在创建的时候选择object
即可自动生成
/**
* 单例类直接在创建的时候选择object,即在类名前方加上object*/
object SingleUtil {
fun printName(name : String){
Log.d("SingleUtil",name)
}
//外部调用SingleUtil.printName("")
}
外部引用 SingleUtil.printName("test")
kotlin中的集合与java中的无异,只是在使用方法上做出很大简便
示例一:添加与删除都有java一样
/**
* kotlin集合类与java类似*/
fun listA(){
val list = ArrayList()
list.add("aaaa")
list.add("bbbb")
list.add("cccc")
}
示例二:listOf
关键字声明的集合具有不可变性,即不能进行删除和添加等修改操作,但同样完成简化,无需使用add
方法进行元素添加
/**
* listOf可简化初始化过程,但为不可变性,即为不能对集合进行添加、删除等操作*/
fun listB(){
val list = listOf("aaaa","bbbb","cccc")
}
示例三: mutableListOf
在listOf
基础上允许对集合进行添加、删除等操作
/**
* mutableListOf在listOf基础上允许对集合进行添加、删除等操作*/
fun listC(){
val list = mutableListOf("aaaa","bbbb")
list.add("cccc")
}
示例四:结合lambda,可以进一步简化代码
/**
* 利用lambda集合式API寻找集合内长度最大的子项*/
fun mapB(){
val list = listOf("aaaa","bbbbb","cccccc")
val lambda = {value : String -> value.length}
val maxLengthA = list.maxOf(lambda)
/**
* 不需要专门定义一个lambda变量,可直接放入函数中*/
val maxLengthB = list.maxOf { value: String -> value.length }
/**
* 由于类型推导机制的存在,可省略变量的类型*/
val maxLengthC = list.maxOf { value -> value.length }
/**
* 当lambda参数列表只剩一个参数时,可用关键字it代替*/
val maxLengthD = list.maxOf { it.length }
/**
* map函数即映射为一个新的元素,最终形成一个新的list集合*/
val upMap = list.map { it.uppercase() }
}
示例五:filter:用于过滤集合中的某些子项,参数类型为boolean
/**
* filter:用于过滤集合中的某些子项,参数类型为boolean*/
fun mapC(){
val list = listOf("aaaa","bbbbb","cccccc")
val newList = list.filter { it.length <= 2 }
.map { it.uppercase() }
/**
* any:用于判断集合类是否至少存在一个满足条件的元素
* 返回类型:boolean*/
val any = list.any { it.length <= 5 }
/**
* all:用于判断集合类是否全部的元素都满足条件
* 返回类型:boolean*/
val all = list.all { it.length <= 5 }
}
示例一: map除了使用标准的put和get方法还可使用类似数组的存储、读取方式
/**
* map除了使用标准的put和get方法还可使用类似数组的存储方式
*如下所示 */
fun mapA(){
val map = HashMap()
map.put("aaaa",1)
var numA = map.get("aaaa")
map["bbbb"] = 2
var numB = map["bbbb"]
for((value,num) in map){
//...
}
}
示例二:同样map集合也可声明可变和不可变类型,其中使用to
关键字来对键值对进行关联
不可变
val mapOf = mapOf("aaaa" to 1,"bbbb" to 2)
可变
val mapMult = mutableMapOf("aaaa" to 1,"bbbb" to 2)
空指针异常在各种编程语言中都是出现频率较高的异常之一,kotlin通过空指针检查优化此缺陷
示例一:此写法代表传入emptyA的参数不能为空,若在外部传入空,系统会给予异常提示,无法通过编译
/**
* 此写法代表传入emptyA的参数不能为空*/
fun emptyA(value : String){
Log.d("empty","value$value")
}
示例二:在参数类型后加上?
,即代表此参数允许为空;在变量后加上?.
即代表若不为空则执行后续表达式,否则不执行
/**
* 此写法代表传入emotyB的参数允许为空*/
fun emptyB(value: String?) {
if (value != null) {
//...
value.lowercase()
}
/**
* 可简化如下*/
value?.lowercase()
}
示例三:?:代表这左右两边都是一个表达式,若前者不为空则返回前者,否则返回后者
/**
* */
fun emptyC(value: String?) : Int{
if (value != null){
return value.length
}
return 0
}
/**
* 简化emptyC
* ?:代表这左右两边都是一个表达式,若前者不为空则返回前者,否则返回后者
* 与三目表达式类似*/
fun emptyD(value: String?) = value?.length ?: 0
示例四:定义一个可为空的全局变量,在emptyE中对其判空处理,在不为空的情况下执行emptyF, 在emptyF中将全局变量转为大写,但此时不能完成编译,因为uppercase方法不知道外部进行类判空处理,所以需要加上非空断言工具!!,即非常确定此处不为
/**
* 定义一个可为空的全局变量,在emptyE中对其判空处理,在不为空的情况下执行emptyF
* 在emptyF中将全局变量转为大写,但此时不能完成编译
* 因为uppercase方法不知道外部进行类判空处理,所以需要加上非空断言工具!!,即非常确定此处不为空,强制编译*/
var sampleA : String? = "hello"
fun emptyE(){
if (sampleA != null){
emptyF()
}
}
fun emptyF(){
sampleA!!.uppercase()
}
示例五:使用let函数调用lambda表达式,不仅可以简化代码,还可以减少代码执行时间复杂度
/**
* 使用if语句进行判空处理,只需执行一次
* 当使用?.时,每调用一次便需要进行一次判空处理*/
fun emptyG(iSample: ISample?){
if (iSample != null){
iSample.doWork()
iSample.doSleep()
}
iSample?.doWork()
iSample?.doSleep()
}
/**
* 简化emptyG函数,使用let函数调用lambda表达式*/
fun emptyH(iSample: ISample?){
iSample?.let { stu->
stu.doWork()
stu.doSleep()
}
iSample?.let {
it.doWork()
it.doSleep()
}
}
在java中需要使用+
去拼接字符串,也可以使用StringBuilder
,但是在字符串内嵌表达式目前java还不支持,可以在string.xml中定义字符串,并需要动态修改的地方加上%1$s
和%1$d
占位符,然后在java代码中找到此资源,然后format
,才能进行内嵌,过程实属麻烦。在kotlin中支持字符串内嵌表达式
示例一:使用$
进行声明,使用{}
进行包裹
fun emptyA(value : String){
Log.d("empty","value${value}")
}
示例二:当只有一个参数时,可省略{}
fun emptyA(value : String){
Log.d("empty","value$value")
}