在Kotlin中存在四种修饰词:public, private,protected,internal.其中默认的修饰词是public。它们用于修饰类、对象、接口、构造函数、属性以及它们的set方法。
在Kotlin中函数、属性、类、对象以及接口可以在包级别进行定义。
四种修饰词在包级别的作用域:
♣ public :默认修饰符,在任何位置都能被访问。
♣ private:只能在当前源文件中被使用
♣ internal:能够在同一个模块中被使用
♣ protected: 无效修饰符,只能用于类和接口内部。
package demo
private fun test1(){} // 只能在当前文件中被访问
public var test2 : String="test" //能在任何位置被访问
internal val test3=6//能够在同一个module中被访问
四种修饰词在类和接口中的作用域:
♣ public: 默认修饰符,在任何位置都能被访问
♣ private: 只能在这个类中被访问
♣ internal: 能够在同一个模块中被使用
♣ protected: 只能被当前类以及子类访问
open class Person{
private val age=15//只能够在当前类中被访问
public val name ="小明"//能够在任何位置被访问
protected val sex="男"//只能够在当前类及其子类中被访问
internal val height=175//能够在同一个Module中被访问
}
所有的构造函数默认是public,当使用其他修饰符修饰时必须明确添加一个constructor关键字:
class Person private constructor (age:Int,name:String){
//...
}
Kotlin中的模块是指一系列的Kotlin文件编译在一起的源码:
♣一个IntelliJ IDEA 模块
♣ 一个Maven工程,或者Gradle工程
♣ 通过Ant任务的一次调用编译的一组文件
局部变量、函数和类是不能指定修饰符的。
与C#和Gosu类似,Kotlin也提供了一种在不继承父类也不使用其他设计模式的情况下对指定的类进行扩展的方法。我们可以通过扩展的特殊声明来实现。扩展分为函数扩展和属性扩展
声明函数扩展时,我们需要在函数前面指定被扩展的对象。例如:
fun MutableList.swap(x:Int,y:Int){//交换list中x项和y项的值
val temp = this[x]//this对应List
this[x] = this[y]
this[y] = temp
}
♣ 在函数扩展中this关键字对应函数接收者对象。
扩展并没有修改被扩展的类。并没有在该类中插入一个新的成员,只是让这个实例对象能够通过“.”调用该扩展成员。
扩展函数是静态解析的,他并不是接收者类型的虚拟成员。意味着调用扩展函数时,具体调用哪个扩展函数由调用函数的对象表达式来决定,而不是动态的类型决定的。例如,我们定义两个对象。Teacher和Person其中Person是Teacher的父类。两对象均声明了扩展函数doFly(),然后声明fly()方法传入Person对象的形参调用doFly方法。
open class Person{}
class Teacher : Person(){}
fun Person.doFly() {//Person类的doFly()扩展方法
println("Person fly")
}
fun Teacher.doFly() { //Teacher类的doFly()扩展方法
println("Teacher fly")
}
fun fly(person : Person){
person.doFly()
}
fun main(args : Array){//测试
var teacher:Teacher = Teacher()
fly(teacher)
}
上述代码的输出结果为”Person fly”,由此可以看出调用哪个扩展函数是由声明的参数类型决定的
当扩展函数的接收者对象存在与扩展函数相同的成员函数。调用该函数时到底调用的是成员函数还是扩展函数:
open class TestDemo {
test(){//成员函数
println("成员函数")
}
}
fun TestDemo.test(){//扩展函数
println("扩展函数")
}
fun main(args : Array<String>){
var person :Person =Person()
person.test()
}
上述代码的输出结果为”成员函数”,由此可以看出当存在成员函数和扩展函数的名称相同时,当调用该函数时,优先调用成员函数。
扩展成员的接收者可以为空。这样我们仍然可以用一个空的对象来调用该扩展成员,在该方法中利用”null==this”的判断进行逻辑操作。这样的话我们可以在任意地方使用该扩展成员而不担心出现空指针的错误。
Kotlin中也支持属性的扩展。
但是由于扩展属性并不能够给接受对象提供一个真正的成员属性,所以扩展属性不能拥有备份字段。因此初始化函数中不能够拥有扩展属性,扩展属性只能够通过明确的getter和setter方法来进行定义。这也意味着扩展属性只能够被声明为val,。如果被声明为var,进行初始化时会报异常错误。
如果定义了伴随对象,则同样可以对伴随对象进行扩展。
open class TestDemo{
companion object{}
}
fun TestDemo.Companion.expandFun(){
}
调用时和普通伴随对象的成员相同:
TestDemo.expandFun()
大多数时候我们在包(top level) 定义扩展:
package test.demo
fun Person.swim(){//在test.demo包下定义swim的扩展
}
当我们在test.demo包外使用上面声明的扩展时,我们需要使用import 关键字导入该扩展
package com.example
import test.demo.swim//导入该扩展
import test.demo.*//也可以通过导入test.demo包下的所有数据的方式导入该扩展
fun main(args : Array<String>){
var person : Person = Person()
person.swim()//使用该扩展
}
我们通常创建一些保存数据的类。在Kotlin中这样的类被称为data类,用data关键字进行标注:
data class Person (val name: String , val age :Int)
编译器会对主构造函数中声明的所有属性添加如下方法:
♣ equals():hashCode 函数
♣ toString():输出的格式为Person(name=小明,age=18)
♣ compontN(): 对应按生命顺序出现的所有属性
♣ copy()
如果在该数据类或其基类中重写了上述方法,则已重写的方法为准
♣ 主构造函数中至少有一个函数
♣ 主构造函数中所有的属性必须标记val或者var关键字
♣ 数据类不能是抽象类、open类、封闭(sealed)类或者内部(inner)类
♣ 数据类不能继承自其他类(接口除外)
♣ 如果数据类有无参构造函数,则需要在主构造函数中将属性初始化。
data class Person(val name:String="小明", val age:Int=18)
当我们需要对数据类中的一些属性进行修改但是保持其他部分不变,则需要使用到copy()函数。例如:
val xiaoming = Person(name="小明",age=18)
val xiaoli = (name="小李")//只是改变了name属性,age属性还是18
上面提到了componentN()组件函数,函数名中的1到N对应属性中的声明顺序,因此我们可以在数据类的多重声明中使用:
val xiaoming = Person("小明", 18)
val (n,m) = xiaoming
println("$n,is $m years old")
上述代码打印结果为”小明,is 18 years old”