类的声明
和 Java 没有什么两样, Kotlin 中, 类的声明也使用 class 关键字, 如果只是声明一个空类, Kotlin 和 Java 没有任何区别, 不过定义类的其他成员, 区别就很大了.
class MyClass{ }
类的构造器
构造器也叫构造方法, 是类创建的必要元素.
1, 主构造器
面向对象语言在定义类的时候, 都需要至少制定一个构造方法, 如果不指定构造器, 编译器会默认生成一个不带任何参数的构造器, 这是传统面向对象语言的做法.
Kotlin 会有一些不一样的地方, 在 Kotlin 中, 类允许定义一个主构造器, 和若干个第二构造器. 主构造器是类头的一部分, 紧跟在类名的后面, 参数是可选的. 如下代码定义了一个类, 并指定了一个主构造器.
- class Person constructor(name: String) {
- }
如果主构造器没有任何注解, 任何修饰, constructor 可以省略:
- class Person(name: String) {
- }
上面的代码只是声明了主构造器, 是在哪里实现构造器呢? 如果是主构造器, 需要在 init 代码块中进行初始化主构造器:
- class Person(name: String) {
- val myName = name
- init {
- println(name)
- }
- }
注意: 主构造器中的参数不仅可以在 init 代码块中使用, 还可以对类的属性进行初始化.
var 和 val 也可以修饰主构造器参数, 如果使用 var, 表示参数对于构造器来说是变量, 在构造器内部可以对其进行操作和改变; 如果使用 val, 表示该参数是常量, 在构造器中不能修改它的值. 但要注意的是, var 修饰的参数, 在主构造器中修改值后, 并不会把修改后的值传到对象外面.
- class Person(var name: String) {
- init {
- name="haha"
- println(name)
- }
- }
2, 第二构造器
Kotlin 的类中, 除了可以声明一个主构造器之外, 还可以声明若干个第二构造器, 第二构造器必须在类中声明, 前面必须加 constructor 关键字.
- class Person(var name: String) {
- init {
- name = "hello"+ name
- println(name)
- }
- constructor(age: Int) : this("js") {
- println(name + " " + age)
- }
- constructor(sex : Byte) :this(20){
- println(name +" "+ sex)
- }
- }
明了主构造器, 那么所有的第二构造器必须在声明的后面调用主构造器, 或者通过另外一个第二构造器间接地调用主构造器.
当然, 如果类中并没有声明主构造器, 第二构造器后面可以不调用主构造器.
上面 Person 类中一共定义了三个构造器, 就涉及到了构造器的重载, 也就是一个类中拥有不同个参数和参数类型的构造器.
注意: 主构造器中可以使用 var 和 val 修饰参数, 但第二构造器中不能使用, 也就意味着第二构造器中的参数都是只读的.
3,Kotlin 的单例模式
我们只是大概了解以下 Kotlin 的单例模式如何书写, 后面会详细介绍.
- class Singleton private constructor() {
- public var value: Singleton? = null
- private object mHolder {
- val INSTANCE = Singleton()
- }
- companion object Factory {
- fun getInstance(): Singleton {
- return mHolder.INSTANCE
- }
- }
- }
4, 函数中的默认参数
有很多变成语言是支持默认参数的, 也就是在调用函数的时候不指定参数值, 就会使用默认的参数值. Java 不支持, 但 Kotlin 是支持的, 先看个例子:
- class Class(param: Int, param1: String = "js") {
- }
在创建 Class 对象时, 可以只传第一个参数, 第二个参数可以不传, 如 Class(5), 其第二个参数默认为 "js".
由于 Kotlin 支持默认参数, 所以没有必要非要定义一个没有参数的构造器, 可以直接定义一个所有参数都有默认值的构造器.
5, 创建类实例
创建 Kotlin 实例在之前已经用到过了. 对于大都数面向对象的语言来说, 创建类实例的时候都会用到 new 关键字, 但 Kotlin 中不再需要, 直接调用构造器即可, 如 MyClass().
类成员
1, 属性的基本用法
Java 开发者对 JavaBean 一定特别熟悉, 其实 JavaBean 就是一个普通的 Javak 类, 关键在于对属性的 get 和 set 方法. 当然 Java 可以直接使用 public 的成员变量来解决这个问题, 但对于属性来说, 不仅仅能读写其值, 还需要对其进行二次加工, 所以 get/set 也是必须的.
Kotlin 中的属性语法, 只有 var/val 和属性名时必须的, 其他都是可选的. 也就是说, Kotlin 属性最简单的形式就是在类中定义一个变量 (var) 或常量(val), 要引用属性, 就像引用变量一样.
- class Class{
- var name:String="js"
- val int:Int=2
- fun pri(){
- println("name=${name} int=${int}")
- }
- }
2, 属性的 get/set 方法
因为 Kotlin 支持属性语法, 所以并不需要对每个单独定义 get 和 set 方法. 如果属性只是可读的 (val), 只需添加一个 get 方法, 如果属性是读写的, 添加 get 和 set 方法. g 如果 get/set 方法只有一行代码, 直接用 = 分隔即可, 如果有多行代码, 则 z 使用{} 处理:
- class Class {
- var name: String
- get() = name
- set(value) {
- name = value
- }
- val age: Int
- get() = age
- }
3, 保存属性值的字段
Kotlin 中可以使用 filed 标识符当作成员变量使用, 也就是通过 filed 读写属性值:
- class Class {
- var name: String = "js"
- get() = field
- set(value) {
- field = value
- }
- }
4, 函数
Kotlin 中, 函数既可以在类外部定义, 也可以在类的内部定义. 如果是前者, 是全局函数, 如果是后者, 是类成员函数. 但无论定义在哪里, 语法都是一样的.
说到构造器时, 构造器支持默认参数值, 实际上, 函数也支持默认参数值. 要注意的是, 带默认值的参数必须是最后几个参数, 也就是说, 如果某个参数带有默认值, 那么该参数后面的所有参数必须都有默认值:
- class Class {
- fun func(url: String, host: String = "www.baidu.com") {
- }
- }
但是如果带默认值的参数过多, 在调的时候也会带来一些麻烦, 如下:
- class Class {
- fun func(url: String, host: String = "www.baidu.com", name: String = "haha") {
- }
- }
当我想调用 func 方法, host 参数使用默认值, 而 name 参数自定义, 就不得不在调用方法的时候将 host 参数显示地传 "www.baidu.com". 为了解决这个问题, Kotlin 允许使用命名参数传递参数值, 所谓命名参数, 就是在调用函数的时候指定形参名(host,name), 这样就可以直接为指定的参数传值了, 如:
Class().func("hh",name="hello")
如果传入函数的参数个数不固定, 可以使用可变参数, 可变参数用 vararg 关键字声明:
- class Class {
- fun func(url: String, host: String = "www.baidu.com", name: String = "haha") {
- }
- fun func2(vararg names: String) {
- for (name in names) {
- println(name)
- }
- }
- }
很多时候, 函数体中只有一行代码, 用传统的写法就比较麻烦了, 这时可以直接在函数声明后加等号(=), 后面直接跟代码, 这种方式可以省略函数返回值类型:
- class Class {
- var value: Int = 1
- fun func(url: String, host: String = "www.baidu.com", name: String = "haha") {
- }
- fun func2(vararg names: String) {
- for (name in names) {
- println(name)
- }
- }
- fun func3() = value
- }
同时, 在函数体内也可以定义函数, 这种函数叫做本地函数, 不再累赘.
5, 嵌套类
所谓嵌套类, 就是直接在 n 类内部再定义一个类.
- class People {
- class Student {
- }
- }
- People.Student()
内部类还可以用 inner 声明, 表示可以通过外部类的实例进行调用:
- class People {
- inner class Student {
- }
- }
- People().Student()
修饰符
Kotlind 的修饰符一共四个: private,protected,internal 和 public.
private: 仅在类的内部可以调用;
protected: 类似 private, 但在子类中也可以访问;
internal: 模块中的任何类都可以调用;
public: 任何类都可以访问.
如果不指定修饰符, 默认全是 public 的, 这些修饰符可以用在普通的类中, 也可以用在构造器中.
类的继承
1, 如何继承
与 Java 不同, Kotlin 类的继承需要使用冒号(:), 而 Java 使用 extends. 注意, 冒号后面需要调用父类的构造器.
Kotlin 和 Java 一样, 都是单继承的, 也就是说, 一个 Kotlin 类只能有一个父类. 要注意的是, Kotlin 类默认是 final 的, 也就是不允许继承的, 需要显示地用 open 关键字声明, 表示此类可以被继承.
- open class School {
- }
- class MiddleSchool : School() {
- }
2, 重写方法
Kotlin 中不仅类默认是不可以被继承的, 连方法默认也是不可以重写的. 因此, 如果要在子类中重写方法啊, 那么父类的对应方法必须用 open 修饰, 而且要在子类重写的方法前面加 override 关键字:
- open class School {
- open fun func() {
- }
- }
- class MiddleSchool : School() {
- override fun func() {
- }
- }
如果一个方法已经被 override 修饰了, 那么这个方法已经就被重写了, 依然可以被它的子类所重写.
3, 重写属性
属性的重写与方法类似, 被重写的属性也必须用 open 修饰, 子类属性必须用 override 修饰. 不过要注意, val 属性可以被重写为 var 属性, 但反过来不可以:
- open class School {
- open val name: String = "School"
- open fun func() {
- }
- }
- class MiddleSchool : School() {
- override var name: String = "MiddleSchool"
- override fun func() {
- }
- }
接口
接口是另一个重要的面向对象元素, 用于制定规范, 强调对象是否具有某个功能.
Kotlin 与 Java 类似, 使用 interface 声明接口, 一个类可以实现多个接口, 实现的方法和类继承相同, 而且, 接口中的属性和方法都是 open 的.
- interface MyInterface {
- fun func()
- fun func2(): String {
- return "js"
- }
- }
- class MyClass : MyInterface {
- override fun func() {
- }
- }
上述代码中可以看出: 实现接口与继承父类类似, 使用冒号(:), 但后面不是调用构造方法, 而是是指跟接口名; Kotlin 中的接口的方法, 允许包含默认方法体, 对于这样的额方法, 子类实现接口时不一定必须实现该方法.
抽象类
抽象类和接口非常相似, 抽象类不能被实例化, 需要 abstract 关键字声明, 抽象类实现接口后, 接口中没有函数体的函数可以不重写, 接口中的这些方法自动被继承到子类中, 称为抽象方法:
- abstract class MyAbsClass{
- abstract fun f()
- }
抽象方法没必要用 open 声明, 因为抽象类本身就是可以被继承的.
小结
Kotlin 中的类 e 和接口与 Java 中的本质上没有什么两样, 只不过 Kotlin 为了体现差异, 加入了一些语法糖, 如接口允许函数带函数体, 支持属性, 不支持静态方法等. 我们需要慢慢去熟悉它.
来源: https://juejin.im/post/5aca2fad6fb9a028c52376f7