运算符重载
kotlin中每一个运算符对应一个方法。比如我们想把两个对象加起来的话,只需要把“+”对应的方法复写就可以实现了。运算符对应的方法可以通过kotlin官网http://kotlinlang.org/docs/reference查询。
fun main(args: Array<String>) {
val p1 = Person()
val p2 = Person()
p1.age = 10
p2.age = 20
//求p1和p2的年龄和
var plus = p1.plus(p2)
println(plus)
}
//重写“+”方法
class Person {
var name = "张三"
var age = 0
fun plus(person: Person): Int {
return age + person.age
}
}
类的成员
类成员可以访问并且修改,kotlin的类成员变量默认已经实现了get以及set方法。
成员的访问与修改
由于kotlin的类成员变量默认实现get以及set方法,所以类中的成员可以直接调用:
fun main(args: Array<String>) {
val stu = Student()
println(stu.age)
println(stu.name)
}
class Student {
val name = "张三"
val age = 15
}
修改类成员的可见性
在Kotlin中,若是想要类成员对于外界只能访问不能修改,则在类成员后中写入 private set。若想类成员对外界既不能访问也不能修改,则在类成员前写入private:
class Student {
var name = "张三"
private set//外界无法修改name
private var age = 15//外界无法访问、修改age
}
自定义访问器
在我们的例子中,学生的年龄不可能随意设置,比如学生是-1岁,所以需要对设置的数据进行判定,符合一定要求后才能修改。对于此类要求,我们可以用自定义访问器field:
fun main(args: Array<String>) {
var person = Student()
println(person.age)//20
person.age = 35//由于35>30,所以设置失败
println(person.age)//20
}
class Student {
var name = "张三"
var age = 20
set(value) {//value对应传递的age值
if (value > 18 && value < 30) {
//可以通过field表示这个字段
field = value
}
}
}
主构函数和次构函数
主构函数
kotlin中主构函数即构造函数,但是无法直接访问构造函数中的元素。如果想要在构造函数里面实现一些操作,需要把代码写在init里:
fun main(args: Array<String>) {
val p1 = Person("张三", 23)
val p2 = Person("李四", 25)
val p3 = Person("王五", 40)
println(p1.age)//23
println(p2.age)//25
println(p3.age)//40
}
class Person(name: String, age: Int) {
var name = ""
var age = 0
init {
this.age = age
this.name = name
}
}
var和val在主构函数参数中的作用
主构函数参数使用var和val,相当于帮我们定义了字段,参数可以直接使用。参数没有var和val修饰,参数在其他地方不能使用 。参数有var修饰,可以使用,也可以修改 。参数有val修饰,只能使用,不能修改 :
fun main(args: Array<String>) {
val p1 = Person("张三", 23)
val p2 = Person("李四", 25)
val p3 = Person("王五", 40)
println(p1.age)
println(p2.age)
println(p3.age)
}
class Person(val name: String,val age: Int)
次构函数
次构函数必须要调用主构函数,必须要把参数传递给主构函数,且需要用constructor关键字:
class Person2(val name: String,val age: Int){
constructor(name: String, age: Int,phone:String):this (name,age)//调用主构函数将参数传递给主构函数
}
次构函数间调用
次构函数可以直接调用主构函数,也可以通过调用次构函数来间接调用主构函数:
class Person3(name:String,age: Int){
//调用主构函数
constructor(name:String,age: Int,phone: String):this(name,age)
//通过调用次构函数来间接调用主构函数
constructor(name:String,age: Int,phone: String,qq:String):this(name,age,phone)
}
次构函数参数使用
构造函数加var和val只能在主构函数里面加,次构函数中不能加。主构函数参数可以直接加上var和val使用,次构函数只能够自己定义变量进行保存。
class Person3(val name:String,val age: Int){
var phone = ""
var qq = ""
constructor(name:String,age: Int,phone: String):this(name,age)
constructor(name:String,age: Int,phone: String,qq:String):this(name,age,phone){
this.phone = phone
this.qq = qq
}
}
继承
继承是指一个对象直接使用另一对象的属性和方法。kotlin的继承和java中的继承大体相同,但需要注意以下几点:
1.需要将父类加上open关键字
2.需要加上():
kotlin的成员函数和成员变量对应的get以及set方法都有final修饰,不能进行继承,所以要在成员函数和成员变量前加上open关键字才能进行复写:
open class father() {
open var name = "小头爸爸"
open var age = 40
open fun sayHello() {
println("老哥 早上好")
}
}
class son : father() {
override var name: String = "大头儿子"
override var age: Int = 14
override fun sayHello() {
println("同学 早上好")
}
}
构造函数的继承
构造函数的继承除了通常的操作外,子类也需要构造函数,并且应能在父类中找到对应的字段,还需将函数传给父类。子类也可以用父类的字段:
fun main(args: Array<String>) {
var p = Person("张三", 30)
var s = Student("李四", 20, "男")
println(p.age)//30
println(s.sex)//男
println(s.name)//李四
}
open class Person(var name: String, var age: Int) {
open fun morning() {
println("早上好")
}
}
class Student(name: String, age: Int, var sex: String) : Person(name, age) {
override fun morning() {
println("晚上好")
}
}
抽象类和抽象方法
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。包含抽象方法的类就是抽象类。抽象类不需要open关键字就可以被调用。
//人类 (抽象类)
abstract class Human {
abstract var color: String
abstract var language: String
abstract fun eat()
}
//具体实现类
class Chinese : Human() {
override var color: String = "yellow"
override var language: String = "chinese"
override fun eat() {
println("用筷子吃饭")
}
}
class USHuman : Human() {
override var color: String = "white"
override var language: String = "english"
override fun eat() {
println("用刀叉吃饭")
}
}
class AfHuman : Human() {
override var color: String = "black"
override var language: String = "葡萄牙语"
override fun eat() {
println("用手吃饭")
}
}
接口
接口的实现是在括号后面加个冒号,然后写上要实现的接口,不需要写interface。若是多个接口,就用逗号“,”隔开。
class men() : bike {
override fun rideBike() {
println("人骑车")
}
}
interface bike {
fun rideBike()
}
注意事项
1.java中接口中的字段一定要被赋值,但是Kotlin接口里的字段不能被赋值
2.java接口里面方法不能实现,kotlin可以实现
class men() : Bike, Drive {
override val license: String = "123456"
override fun drive() {
println("人开车")
}
override fun rideBike() {
println("人骑车")
}
}
interface Bike {
fun rideBike()
}
interface Drive {
val license: String
fun drive() {
println("放手刹,踩油门,走")
}
}
多态
同种功能的不同表现形式。kotlin中的多态原理同java:
abstract class Animal {
abstract var name: String
open fun bark() {
println("动物叫")
}
}
class Dog : Animal() {
override var name: String = "旺财"
override fun bark() {
println("旺财 汪汪叫")
}
}
class Cat : Animal() {
override var name: String = "汤姆"
override fun bark() {
println("汤姆 喵喵叫")
}
}
智能类型转换
在多态中,多个子类可能均与父类有差别。kotlin中可以通过智能类型推断来判断调用的字段所属于的子类,最终调用所属子类的方法。将父类引用转为子类引用,转换之前还需要判断是否是当前子类类型。一旦判断出是这个类型之后,编译器就把类型转换过去了,不需要手动转换。
fun main(args: Array<String>) {
val shepHerdDog: Dog = ShepHerdDog()//将父类引用转为子类引用
//判断是否是当前子类类型
if (!(shepHerdDog is ShepHerdDog)) return
//编译器自动转换
shepHerdDog.shepHerd()
}
abstract class Dog
class ShepHerdDog : Dog() {
fun shepHerd() {
println("牧羊犬开始放羊了...")
}
}
嵌套类、内部类
嵌套类默认都是static修饰的,属于静态类,不依赖于外部的环境,即和外部类没有关系:
fun main(args: Array<String>) {
val inClass = outClass()
}
class outClass {
var name: String = "李四"
class inClass {
fun say() {
println(name)
}
}
}
内部类需要通过Inner修饰,且需要先创建出来外部环境:
fun main(args: Array<String>) {
val inClass = outClass.inClass()
}
class outClass {
var name: String = "李四"
inner class inClass {
fun say() {
println(name)
}
}
}
内部类中使用this
如上例,若内部类中也有一个var name = "张三",则默认情况下会打印张三,即从最近开始找。如果我们想访问外部类中的name,也就是李四。这时候就需要把打印内容改为外部类名.this.name或this@outClass.name:
class OutClass {
var name = "李四"
inner class InClass {
var name = "张三"
fun sayHello() {
println(this@OutClass.name)
//或
println(OutClass.this.name)
}
}
}