Kotlin基础知识_01-类_对象

Kotlin基础知识-01-类_对象

1. 创建类并赋值

class Person {
    var name = ""
    var age = 0

    fun eat() {
        println(name + " is eating. He is " + age + " years old.")
    }
}

fun main() {
    val p = Person()
    p.name = "Zhang san"
    p.age = 20
    p.eat()
}

输出:

Zhang san is eating. He is 20 years old.

Note: nameage 没有加任何访问修饰符,默认为 public的.

2. 继承、主构造函数、次构造函数、init

2.1 继承

open class Person {
    var name = ""
    var age = 0

    fun eat() {
        println(name + " is eating. He is " + age + " years old.")
    }
}

class Student : Person() {
    var sno = ""
    var grade = 0
}

fun main() {
    val stu = Student()
    stu.name = "Lisi"
    stu.age = 10
    stu.eat()
}

运行:

Lisi is eating. He is 10 years old.

Note:

  1. Kotlin中的类(除抽象类外)默认均不可继承,必须手动加上 open关键字,声明该类可继承。
  2. Student : Person() 中,Person后面加了括号,说明调用了父类的无参构造函数;

2.2 主构造函数

class Student(val sno: String, val grade: Int) : Person()

Note:

  1. Kotlin中的主构造函数是最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然也可以显式地给它指明参数,主构造函数直接定义在类名的后面,没有函数体。

2.3 init

如果要在主构造函数中编写逻辑,可使用 init 代码块:

open class Person {
    var name = ""
    var age = 0

    fun eat() {
        println(name + " is eating. He is " + age + " years old.")
    }
}

class Student(sno: String, grade: Int) : Person() {
    init {
        println("The sno: $sno")
        println("The grade: $grade")
    }
}

fun main() {
    val stu = Student("A1818", 20)
    stu.name = "Lisi"
    stu.age = 10
    stu.eat()
}

运行:

The sno: A1818
The grade: 20
Lisi is eating. He is 10 years old.

2.4 次构造函数

open class Person(val name: String, val age: Int) {

    fun eat() {
        println("$name is eating. He is $age years old.")
    }
}

class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {

    constructor(name: String, age: Int) : this("", 0, name, age) {}
    constructor() : this("unknown", 0)

    fun info() {
        println("sno: $sno, grade is $grade, name: $name, age: $age")
    }
}

fun main() {
    val stu0 = Student()
    stu0.info()

    val stu1 = Student("ana", 20)
    stu1.info()

    val stu2 = Student("A1819", 100, "lil", 20)
    stu2.info()
}

运行:

sno: , grade is 0, name: unknown, age: 0
sno: , grade is 0, name: ana, age: 20
sno: A1819, grade is 100, name: lil, age: 20

Note:

  1. 使用 constructor 关键字来声明次构造函数;
  2. Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用);
  3. 当在主构造函数中将参数声明成val或者var时,该参数将自动成为该类的字段,如上述的 val sno: String, val grade: Int
  4. 其中,name: String, age: Int 不能加上 valvar关键字,因为这会和父Person中已定义的成员属性冲突。

2.5 只有次构造函数,没有主构造函数

类中只有次构造函数,没有主构造函数。这种情况真的十分少见,但在Kotlin中是允许的。当一个类没有显式地定义主构造函数且定义了次构函数时,它就是没有主构造函数的。

class Boy: Person {
    constructor(name: String, age: Int): super(name, age) {}
}

fun main() {
    val boy = Boy("zhangSan", 10)
    boy.eat()
}

运行:

zhangSan is eating. He is 10 years old.

PS: 这种写法其实没有多大必要性,即使这样写了 IDEA也会报:
Secondary constructor should be converted to a primary one

按IDEA的提示转换后,代码会变成:

class Boy(name: String, age: Int) : Person(name, age) {
}

3. 接口

3.1 创建接口的方式

interface Hobby {
    fun sing()
    fun jump()
    fun rap() {
        println("I'm rapping....")
    }
}

Note:接口中的函数可以有函数体也可以没有,没有函数体的话在继承时需要强制实现,有函数体的话,则不需要强制实现。

3.2 实现接口

class Boy(name: String, age: Int) : Person(name, age), Hobby, Sport {
    override fun sing() {
        println("$name like sing.")
    }

    override fun jump() {
        println("$name like jump.")
    }

    override fun playBall() {
        println("$name like play ball.")
    }
}

interface Sport {
    fun playBall()
}

interface Hobby {
    fun sing()
    fun jump()
    fun rap() {
        println("I'm rapping....")
    }
}


fun main() {
    val boy = Boy("zhangSan", 10)
    boy.sing()
    boy.jump()
    boy.rap()
    boy.playBall()
}

运行:

zhangSan like sing.
zhangSan like jump.
I'm rapping....
zhangSan like play ball.

Note:

  1. 实现接口直接使用 :号连接即可,不要使用 implements 关键字, kotlin中继承父类和实现接口均使用 :符号;
  2. 可以实现多个接口,多个接口间用,号分隔;
  3. 实现接口不需要给接口名加上(), 因为接口没有构造函数。
  4. kotlin 使用 override 关键字来重写父类或接口中的函数。

4. 访问修饰符 - public, private, protected, internal

与 java 中 默认的4种访问修饰符区别:

修饰符 Java Kotlin
public 所有类可见 所有类可见(默认)
private 当前类可见 当前类可见
protected 当前类、子类、同一包路径下的类可见 当前类、子类可见
default 同一包路径下的类可见(默认) 没有
internal 没有 同一模块中的类可见

Note: 主要有以下区别:

  1. java 默认访问修饰符若不写则为包级的,kotlin 默认为 public的;
  2. kotlin中的protected 访问修饰符缩小了作用范围,只在当前类和子类中可见;
  3. kotlin中新引入了一个 internal访问修饰符,用于模块内可见。比如我们开发了一个lib给别人使用,但是有一些函数只允许在lib内部调用,不想给外部使用,就可以将这些函数声明成internal。

5. 数据类和单例类

5.1 数据类(data class)

数据类也叫实体类,java 里面就是一个domain对象,一般这样定义:

public class Man {

    private String mName;
    private int mAge;
    private String mAddress;

    public String getName() {
        return mName;
    }

    public void setName(String mName) {
        this.mName = mName;
    }

    public int getAge() {
        return mAge;
    }

    public void setAge(int mAge) {
        this.mAge = mAge;
    }

    public String getAddress() {
        return mAddress;
    }

    public void setAddress(String mAddress) {
        this.mAddress = mAddress;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Man man = (Man) o;
        return mAge == man.mAge && Objects.equals(mName, man.mName) && Objects.equals(mAddress, man.mAddress);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mName, mAge, mAddress);
    }

    @Override
    public String toString() {
        return "Man{" +
                "mName='" + mName + '\'' +
                ", mAge=" + mAge +
                ", mAddress='" + mAddress + '\'' +
                '}';
    }

}

一般都要实现 getter/setter 方法 ,如果需要对两个对象进行比较,还需要实现 equals()、hashCode()、toString()等方法。

kotlin中实现:

data class Man(val name: String, val age: Int, val address: String)

fun main() {
    val man1 = Man("Li", 20, "Beijing")
    println("[man1], name: ${man1.name}, age: ${man1.age}, address: ${man1.address}")
    val man2 = Man("Li", 20, "Beijing")
    println("[man2], name: ${man2.name}, age: ${man2.age}, address: ${man2.address}")
    println("man1 == man2: ${man1 == man2}")
}

运行:

[man1], name: Li, age: 20, address: Beijing
[man2], name: Li, age: 20, address: Beijing
man1 == man2: true

只要使用 data关键字声明就可以,该类会自动成为数据类,且自动带 getter/setter 方法, 自动实现 equals()、hashCode()、toString() 等固定且无实际逻辑意义的方法。

Note: 当一个类中没有任何代码时,还可以将尾部的大括号省略。上面的数据类就没有带大括号。

5.2 单例

java 单例:

public class Singleton {

    private static Singleton instance;

    private Singleton() {}

    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public void singletonTest() {
        System.out.println("singletonTest is called.");
    }
}

一般单例如上述写法(注意,不严谨,最好用双重检查锁的方式)。

kotlin 单例:

object Singleton {
    fun singletonTest() {
        println("singletonTest is called.")
    }
}

fun main() {
    Singleton.singletonTest()
}

运行:

singletonTest is called.

Note: 只要用 object 关键字声明即可,kotlin 保证该类的对象在JVM中是唯一的。

posted @ 2023-10-27 11:22  夜行过客  阅读(67)  评论(0编辑  收藏  举报