目录
四 类与对象
继承与构造函数
接口
函数的可见性修饰符
数据类和单例类
在kotlin创建一个类的代码如下所示
class Person{
var name=""
var age=0
fun eat(){
print(name)
print(age)
}
对这个类进行实例化的代码如下所示
val p=Person()
实例化的方法和Java基本类似,只是去掉了new关键字。上述代码中p就可以称为Person类的实例,也可以称为一个对象。可以对p对象进行一些操作,如下所示
p.name="jack"
p.age=10
p.eat()
上述代码将p对象的姓名赋值为Jack,年龄赋值为10,然后调用它的eat()函数,可以打印出这个人的姓名年龄的信息。
在kotlin中,任何一个非抽象类默认都是不可被继承的,相当于Java中给类声明了final关键字。如果想让上述的Person类被继承,必须在Person类的前面加上open关键字。如下所示
open class Person{
...
}
在Java中继承的关键字为extens,在kotlin中变成一个冒号,如下所示
class Student : Person(){
var sno=""
var grade=0
}
这里有一个小细节,那就是继承的时候在Person类后面加上一对小括号,而Java中的继承并不需要括号。这个括号涉及构造函数的知识。
任何一个面向对象的语言都会有构造函数的概念,在kotlin中,构造函数分成了两种,主构造函数和次构造函数。
主构造函数是最常见的构造函数。每个类都会默认有一个不带参数的主构造函数,当然也可以显式的给它指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即可。如下所示
class Student(val sno:String,val grade:Int):Person(){
}
当将字段放进主构造函数的时候,要求在对该类实例化的时候,必须传入构造函数中要求的所有参数。如下所示
val student=Student("123",6)
如果想在主构造函数中编写一些逻辑,kotlin给我们提供了一个init结构体,如下所示
class Student(val sno:String,val grade:Int):Person(){
init{
print(sno)
print(grade)
}
}
在继承特性上,子类的构造函数必须调用父类的构造函数。观察上述代码,Student类声明的主构造函数必须调用父类的构造函数,但是这个时候我们发现父类的主构造函数并没有函数体。而kotlin采用了括号的方式实现了这一点,子类的主构造函数调用父类的哪个构造函数,在继承的时候通过括号来指定。因此这里表示Student类的主构造函数在初始化的时候会调用Person类的无参构造函数,即使在无参的时候,这对括号也不能省略。当然也可以修改Person的主构造函数,让它有参数。如下
open class Person(val name:String,val age:Int){
...
}
这个时候也必须改一下Student类的主构造函数,如下
class Student(val sno:String,val grade:Int,name:String,age:Int):
Person(name,age){
...
}
这里有一个小细节,在子类的主构造函数中增加父类的字段的时候,不能在前面加val或者var关键字,因为在主构造函数中将参数声明成val或者var,那么这个参数会自动变成该类的字段。就会导致和父类中的同名字段冲突。那么现在就可以通过如下方式创建一个Student类的实例。
val student = Student("123",5,"Jack",10)
接下来kotlin中构造函数的另一个部分,次构造函数。任何类都只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可以用于实例化一个类,只不过它拥有函数体。当一个类既有主构造函数又有次构造函数的时候,所有的次构造函数都必须调用或间接调用主构造函数。如下代码
class Student(val sno:String,val grade:Int,name:String,age:Int):
Person(name,age){
constructor(name:String,age:Int):this("",0,age,name){}
constructor():this("",0){}
...
}
kotlin中次构造函数是通过constructor关键字定义。第一个次构造函数接收name和age参数,通过this关键字调用主构造函数。将sno和grade参数赋值为初始值。第二个次构造函数不接受任何参数,通过this关键字调用第一个次构造函数,并将age和name也赋值为初始值。
还有一种比较特殊的情况,那就是类中只有次构造函数没有主构造函数,这在kotlin中也是允许的。当一个类没有显式的定义主构造函数并且定义了次构造函数,它就是没有主构造函数的,如下代码所示
class Student : Person {
constructor(name:String,age:Int):super(name,age){}
}
在这里,Student类的后面没有显式的定义主构造函数,同时定义了次构造函数,所以现在Student类是没有主构造函数的,没有主构造函数,在继承Person的时候的时候也就不需要加上括号了。也由于没有主构造函数,次构造函数在调用父类构造方法的时候就不能使用this了,而是要使用super。
但是其实次构造函数几乎是用不到的,kotlin提供了一个给函数设定参数默认值的功能。如下代码
class Student(val sno:String=“”,val grade:Int=0,name:String=“”,age:Int=0):
Person(name,age){
...
}
只需要在主构造函数中给每个参数都设置默认值,就可以使用任何传参组合来对Student类进行实例化。
接口是用于实现多态编程的重要组成部分,Java是单继承结构的语言,一个类最多只能继承一个父类,但是却可以实现任意多个接口,kotlin也一样。kotlin中创建一个接口的关键字为interface,如下
interface Study{
fun read()
fun doHomework()
}
kotlin中实现接口的关键字和继承一样,也是冒号,但是实现接口不用加括号,因为它没有构造函数。如下
class Student(name:String,age:Int):Person(name,age),Study{
override fun read(){
print(name+"is read")
}
override fun doHomeWork(){
print(name + "is doing homework")
}
}
Study接口中定义了两个待实现方法,因此Student中必须实现接口中的函数。我们来调用一下这个接口中的两个方法。如下所示
fun main(){
val student=Student("jack",10)
doStudy(student)
}
fun doStudy(study:Study){
study.read()
study.doHomework()
}
首先我们创建一个Student的实例,这里是可以直接调用该实例的方法。由于Student类实现了Study接口,因此Student的实例,是可以传递给Study参数的然后调用Study接口的两个函数,如果传入的实例不一样,接口的函数展示的行为也不一样。这种叫面向接口编程,也叫多态。
Java在jdk 1.8 之后,允许对接口中定义的函数进行默认实现,kotlin也支持这一功能。当接口中的函数有了默认实现之后,当一个类去实现该接口时,就可以自由选择是否实现该函数。
Java中的可见性修饰符有public,protected,private,default,kotlin中的可见性修饰符有public,protected,private,internal。它们之间的区别联系如下图所示。
修饰符 | Java | Kotlin |
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
proteced | 当前类、子类、同一包路径下可见 | 当前类,子类可见 |
default | 同一包路径下可见(默认) | 无 |
internal | 无 | 同一模块中的类可见 |
数据类用于将服务器端或者数据库中的数据映射到内存中,为项目提供数据模型的支持。像MVVM的架构模式中的M就是数据类。在Java中,数据类需要重写equals()、hashcode()、toString()这几个方法。重写equals就必须重写hashcode,否则会导致hashmap等hash相关的系统类无法正常工作。但是在kotlin中只需要加上data关键字,如下所示
data class Cellphone(val brand:String,val price:Double)
当一个类中没有任何代码时,可以将大括号省略。
fun main(){
val cell1=Cellphone("123",19)
val cell2=Cellphone("123",19)
println(cell1)
println(cell1==cell2)
}
这里输出的结果应该是true,若没有data关键字,则得到不同的结果。
Java中的单例类实现并不是很复杂,如下代码
public class Singleton {
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance(){
if (instance==null){
instance = new Singleton();
}
return instance;
}
}
而在kotlin中,只需要如下
object Singleton{
}
如果要调用单例类中的方法,只需要类名.方法名即可,类似Java中的静态方法的调用方式。但其实kotlin在背后自动创建了一个Singleton的实例,并且保证全局只会存在一个。