Java转Kotlin:类和接口

·  阅读 289

1 类的定义

1.1 类的定义

//Java
public class Person {
    
}
复制代码
//Kotlin
class Person
复制代码

类的定义中,Kotlin与Java对比有以下异同(假设读者熟知Java):

  • 都使用class关键字;
  • 都使用{}大括号包裹类的body;

  • Kotlin默认为public,省略不写为public,可以加上privateprotected修饰;
  • 若类无body,则可以省略掉{}不写;

1.2 属性定义

//Java
public class Person {
    public String name;
    public int age;
}
复制代码
//Kotlin
class Person {
    var name: String = ""
    var age: Int = 0
}
复制代码

Java中,类中定义的变量称为字段(Field);Kotlin中,类中定义的变量称为属性(Property)。需要注意的是,Kotlin中的所有的属性在定义的时候都必须手动初始化(或指明初始化的时机),否则报错;而Java中的字段不手动初始化都会具有默认的初始值。

1.3 函数(方法)定义

//Java
public class Person {
    public String name;
    public int age;
    
    public void introduceSelf() {
        System.out.println("Hello! I am " + name + ", and I am " + age + "years old.");
    }
}
复制代码
//Kotlin
class Person {
    var name: String = ""
    var age: Int = 0
    
    fun introduceSelf() {
        println("Hello! I am $name and I am $age years old.")
    }
}
复制代码

Kotlin类中函数(方法)的定义与普通函数的定义没有区别,使用fun关键字,接函数(方法)名,接:冒号,接返回值类型,接{}包裹的函数体即可,:Unit可以省略不写,详见《Java转Kotlin:函数基础》

1.4 构造器定义

//Java
public class Person {
    public String name;
    public int age;
    Person(name, age) {
        this.name = name;
        this.age = age;
    };
}
复制代码
//Kotlin
class Person(var name: String, var age: Int) {
    
}
复制代码

Kotlin中有主构造器副构造器的区别,并且规定:所有的副构造器必须调用主构造器。像上面直接写在类名后面的就是主构造器。

2 接口的定义

2.1 接口的定义

//Java
public interface Act {
    void introduceSelf();
    void walk();
}
复制代码
//Kotlin
interface Act {
    fun introduceSelf()
    fun walk()
}
复制代码

2.2 接口的实现

//Java
public Person implements Act {
    @Override
    void introduceSelf() {
        //TODO
    }
    @Override
    void walk() {
        //TODO
    }
}
复制代码
//Kotlin
class Person(var name: String, var age: Int) : Act {
    override fun introduceSelf() {
        //TODO
    }
    override fun walk() {
        //TODO
    }
}
复制代码

Java中的接口实现使用implements关键字;

Kotlin中的接口实现使用:

Java中的接口实现使用@Override注解,起到提示性作用;

Kotlin中接口实现必须使用override关键字,是强制性的。

3 抽象类的定义

3.1 抽象类的定义

//Java
abstract class Person implements Act {
    public String name;
    public int age;
    public String job;
    public Person(String name, int age, String job) {
        this.name = name;
        this.age = age;
        this.job = job;
    }
    public abstract void think();
    public void learn() {}
    public void talk() {}
}
复制代码
//Kotlin
abstract class Person(var name: String, var age: Int, var job: String) : Act {
    abstract fun think()
    fun learn() {}
    open fun talk() {}
}
复制代码

3.2 抽象类的实现

//Java
public Teacher extends Person {
    public Teacher(String name, int age, String job) {
        super(name, age, job);
    }
    
    @Override
    public void think() {
        System.out.println("Teacher" + this.name + "is thinking.");
    }
    
    @Override
    public void learn() {
        System.out.println("Teacher" + this.name + "is learning.");
    }
    
    @Override
    public void talk() {
        System.out.println("Teacher" + this.name + "is talking.");
    }
}
复制代码
//Kotlin
abstract class Person(var name: String, var age: Int, var job: String) : Act {
    abstract fun think()
    fun learn() {}
    open fun talk() {}
}

open class Teacher(name: String, age: Int, job: String, var grade: Int) : Person(name, age, job) {

    override fun introduceSelf() {
        println("Hello! My name is $name. I'm a $job, I teach grade $grade and I'm $age years old.")
    }

    override fun walk() {
        println("Teacher $name is walking.")
    }
    
    override fun think() {
        println("Teacher $name is thinking.")
    }

    override fun talk() {
        println("Teacher $name is talking.")
    }
}
复制代码

注意open关键字:在Kotlin中,若子类想要重写父类的方法,则要求父类的方法被open关键字修饰;若子类重写了父类的方法之后,不希望自己的子类再重写该方法,则可用final关键字,拒绝子类重写方法,如果没有final关键字,open关键字的属性将得以传递。即在类里面定义方法时,默认是final的。

上图中的learn()方法没有open关键字,所以子类Tearcher无法重写该方法。

4 重要知识点

4.1 属性与字段

Java定义在类中的变量都被称为字段(Field);

Kotlin定义在类中的varval都被称为属性(Property)。

二者有什么区别呢?

Property = Field + Setter + Getter

例如,我们在接口中定义一个属性:

//Kotlin
interface Act {
    var actCount: Int
    fun introduceSelf()
    fun walk()
}
复制代码

实现该接口的类要么是抽象类,要么需要实现该属性的set()get()方法:

//Kotlin
open class Teacher(name: String, age: Int, job: String, var grade: Int) : Person(name, age, job) {
    override fun think() {
        println("Teacher $name is thinking.")
    }

    override fun introduceSelf() {
        println("Hello! My name is $name. I'm a $job, I teach grade $grade and I'm $age years old.")
    }

    override fun walk() {
        println("Teacher $name is walking.")
    }

    override fun talk() {
        println("Teacher $name is talking.")
    }

    override var actCount: Int = 2
        get() {
            return field
        }
        set(value) {
            field = value
        }
}
复制代码

代码中的field在IDE中被加粗显示,这个field被称为backing field,称为幕后字段,因此,可以说明属性Property与字段Field的关系。

4.2 属性引用

//Kotlin
package imooc.chapter_4

//TODO:定义一个接口

interface Act {
    var actCount: Int
    fun introduceSelf()
    fun walk()
}

//TODO:定义一个类

abstract class Person(var name: String, var age: Int, var job: String) : Act {
    abstract fun think()
    fun learn() {}
    open fun talk() {}
}

open class Teacher(name: String, age: Int, job: String, var grade: Int) : Person(name, age, job) {
    override fun think() {
        println("Teacher $name is thinking.")
    }

    override fun introduceSelf() {
        println("Hello! My name is $name. I'm a $job, I teach grade $grade and I'm $age years old.")
    }

    override fun walk() {
        println("Teacher $name is walking.")
    }

    override fun talk() {
        println("Teacher $name is talking.")
    }

    override var actCount: Int = 4
        get() {
            return field
        }
        set(value) {
            field = value
        }
}

open class Policeman(name: String, age: Int, job: String, var cases: Int) : Person(name, age, job) {
    override fun think() {
        println("Teacher $name is thinking.")
    }

    override var actCount: Int = 3
        get() = field
        set(value) {
            field = value
        }

    override fun introduceSelf() {
        println("Hello! My name is $name. I'm a $job, I've solved $cases cases and I'm $age years old.")
    }

    override fun walk() {
        println("Policeman $name is walking.")
    }
}

fun main() {
    val mike = Policeman("Mike", 23, "Policeman", 56)
    mike.introduceSelf()
    mike.walk()

    //TODO:属性引用
    val actCountRef1 = Policeman::actCount
    actCountRef1.set(mike, 3)//actCountRef1未绑定Receiver,因此需要传递一个Receiver进来
    val actCountRef2 = mike::actCount
    actCountRef2.get()//actCountRef2绑定了Receiver mike,因此不需要传递Receiver进来
}
复制代码

我们着重看一下main()函数,注意属性引用的部分,这与Java的反射有相似的味道。

5 本节回顾

分类:
代码人生
标签:
分类:
代码人生
标签:
收藏成功!
已添加到「」, 点击更改