01 Kotlin 的变量、函数和类型
为项目添加 Kotlin 语言的支持
如果是现有的项目要支持 Kotlin,只需要像下面这样操作,把两个 build.gradle
中标注的代码对应贴到项目里就可以了。
-
项目根目录下的
build.gradle
: -
app 目录下的
build.gradle
:
Kotlin定义变量
- var: variable的缩写,声明变量的修饰符
var view: View
- val: value的缩写,声明只读变量的修饰符,只能复制一次,不能修改
val a: Int=10
Kotlin类型推断
如果声明时直接赋值,则可以不写变量类型。例如:val a=10
注意:类型推断与动态类型不同,如下所示。
- 动态类型:变量类型在运行时可改变。
- 类型推断:代码中不用写变量类型,编译器会帮忙补上。
因此,Kotlin 是一门静态语言。
Kotlin 空安全设计
空安全设计的目的:为了防止调用空对象,出现NullPointerException错误 Kotlin中所有变量不允许为空
lateinit
作用:让IDE不要对这个变量进行检查和报错 适用场景:我很确定这个变量到使用的时候不为空,但是在声明的时候暂时还不能给它赋值
lateinit var view: View
可空类型:类型后加 ?
作用:解除变量的非空限制
var name:String? = null
注意:"?"包含了非空检查.
Java中使用可能为空的变量时,我们一般会通过写if(name==null)来检查变量是否为空。但是if(name==null)检查不一定能保证name不为空,因为在多线程的情况下,别的线程可能会在检查非空后,将name的值变为null。
Kotlin中使用可能为空的变量的做法:(线程安全)
class User{
var name:String? = null
}
...
println(name?.length) //会对变量做一次非空确认后再调用方法
println(name!!.length) //也可以用双感叹号,写法不同而已
总结:报错情况
- 变量需要手动初始化,所以不初始化会报错
- 变量默认非空,所以初始化时赋值为null也报错
- 用?设置的可空变量使用时报错
注意:讨论可空/不可空,都是针对变量在使用时的情况
Kotlin的函数声明
- 以 fun 关键字开头
- 返回值写在了函数和参数后面
fun cook(name: String): Food {
...
}
如果没有返回值:
fun main(): Unit {}
// Unit 返回类型可以省略
fun main() {}
注意:函数参数有可空控制,传参时注意可空/不可空的匹配
// 👇可空变量传给不可空参数,报错
var myName : String? = "rengwuxian"
fun cook(name: String) : Food {}
cook(myName)
// 👇可空变量传给可空参数,正常运行
var myName : String? = "rengwuxian"
fun cook(name: String?) : Food {}
cook(myName)
// 👇不可空变量传给不可空参数,正常运行
var myName : String = "rengwuxian"
fun cook(name: String) : Food {}
cook(myName)
Kotlin中的属性的 getter/setter 函数
实例:在如下例子中,调用 hello.name
方法 , 实际上调用的是 hello.setName
方法
class Hello {
var name = "Tom"
var age = 18
}
fun main() {
var hello = Hello()
hello.name = "Jack"
}
幕后字段
在Kotlin中属性作为一级语言特性,通常情况下集幕后字段(field储值变量)+ 访问器(getter读访问器、setter写访问器)于一身,无论是声明【var age: Int】、赋值【user.age = 18】、取值【println(user.age)】,从字面上看都是 age 这个属性本身,是一个整体。而只有在我们需要自定义访问器的时候才会区分这三者。
例如自定义 setter 的时候,如果不写成幕后字段 field = value,不管是 age = value 还是 this.age = value 都会报错,因为属性 age 的赋值就是setter,显然不能递归调用。
如果属性的访问器至少有一个使用默认实现,或者自定义的访问器中使用了 field ,那么就会提供幕后字段,用 field 关键字表示,主要用于自定义 getter/setter 时使用,也只能在 getter/setter 中访问。
实例:
class Demo{
var id: Long = 0
get() = field
set(value) { field = value }
}
个人理解:field是一个中转变量
Kotlin中的基本类型
在 Kotlin 中,所有东西都是对象,Kotlin 中使用的基本类型有:数字、字符、布尔值、数组与字符串。
var number: Int = 1 // 👈还有 Double Float Long Short Byte 都类似
var c: Char = 'c'
var b: Boolean = true
var array: IntArray = intArrayOf(1, 2) // 👈类似的还有 FloatArray DoubleArray CharArray 等,intArrayOf 是 Kotlin 的 built-in 函数
var str: String = "string"
什么是装箱?
简单来说,原先在 Java 里的基本类型,类比到 Kotlin 里面,条件满足如下之一就不装箱:
- 不可空类型。
- 使用 IntArray、FloatArray 等。
Kotlin中的类与对象
Kotlin写法和Java写法的不同
//Kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
}
}
//Java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
}
}
写法对比
对比点 | Java | Kotlin |
---|---|---|
可见性 | 需要写明 | 默认为public,可以省略 |
类的继承的写法 | 用extends | 用: |
构造方法的写法 | public MainActivity() { } (可省略) | 单独用了一个 constructor 关键字来和其他的 fun 做区分:class MainActivity constructor() : AppCompatActivity() { } |
override | @Override 是注解的形式 | override 变成了关键字,省略了 protected 关键字,也就是说,Kotlin 里的 override 函数的可见性是继承自父类的 |
类的继承的写法 | MainActivity 可继承,因为Java 里只有加了 final 关键字的类才是 final 的。 | MainActivity 无法继承(无法作为父类),因为Kotlin 里的类默认是 final 的 |
实例化一个类 | Activity activity = new NewActivity(); | var activity: Activity = NewActivity() |
Kotlin的open
关键字
Kotlin的类默认不可继承,所以要想让Kotlin里的MainActivity可以继承:使用open
关键字
open class MainActivity : AppCompatActivity() {}
但是要注意,此时 NewActivity 仍然是 final 的,也就是说,open
没有父类到子类的遗传性。
override
是有遗传性的,如果要关闭override
的遗传性,只需要这样即可:
open class MainActivity : AppCompatActivity() {
// 👇加了 final 关键字,作用和 Java 里面一样,关闭了 override 的遗传性
final override fun onCreate(savedInstanceState: Bundle?) {
...
}
}
Kotlin的abstract
关键字
Kotlin中abstract
关键字修饰的类无法直接实例化,并且通常来说会和 abstract
修饰的函数一起出现:
abstract class MainActivity : AppCompatActivity() {
abstract fun test()
}
但是子类如果要实例化,还是需要实现这个 abstract 函数的:
class NewActivity : MainActivity() {
override fun test() {}
}
Kotlin中的类型的判断和强转
Kotlin中使用 is
关键字进行「类型判断」,并且因为编译器能够进行类型推断,可以帮助我们省略强转的写法:
//Kotlin中的写法
fun main() {
var activity: Activity = NewActivity()
if (activity is NewActivity) {
// 👇的强转由于类型推断被省略了
activity.action()
}
}
//相当于Java中这么写
void main() {
Activity activity = new NewActivity();
if (activity instanceof NewActivity) {
((NewActivity) activity).action();
}
}
Kotlin中类的强转调用:使用 as
关键字
fun main() {
var activity: Activity = NewActivity()
(activity as NewActivity).action()
}
更安全的强转写法:使用 as?
,可以更优雅地处理强转出错的情况。
fun main() {
var activity: Activity = NewActivity()
// 👇'(activity as? NewActivity)' 之后是一个可空类型的对象,所以,需要使用 '?.' 来调用
(activity as? NewActivity)?.action()
}
转载自:https://juejin.cn/post/7254792532651065381