类
类定义
class Empty{// 与java定义类相似
fun a(){
}
}
//但 Kotlin 可定义空类
class Empty
类属性
/**
*类可以有一个主构造器以及一个或多个次构造器
*主构造器位于类名后
*如果主构造器没有任何注解,也没有任何可见修饰符,则可省略(注解和修饰符放在constructor前)
**/
class Person constructor(firstName:String){//class Person(FirstName:String)
val name:String //不可变变量
var age //可变变量
}
//用构造函数创建类实例,kotlin中没有关键字new
empty = new Person()
//引用类属性
empty.name
getter & setter
/*
属性声明的完整语法
*/
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
//val不允许设置setter函数,因为它不可修改
//如果属性类型可从初始化语句或类成员函数推断出来,则可省略类型
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter
val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter
实例
class Person {
var lastName: String = "zhang"
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
var no: Int = 100
//Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
var heiht: Float = 145.4f
private set
lateinit var m:Int //延迟初始化,否则一定要定义时初始化
}
主构造器
主构造器中不能含任何代码,初始化代码可以放在初始化代码段,初始化代码段以init作为前缀
class Person constructor(firstName:String){
init{
...
}
}
/*
可通过主构造器定义属性并初始化
*/
class Person constructor(val firstName:String, var age:Int){
}
次构造器
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:
class Person(val name: String) {
constructor (name: String, age:Int) : this(name) {
// 初始化...
}
}
如果一个非抽象类没有声明构造函数,它会自动生成一个无参数的public构造函数。如果你不想这个类有public构造函数,则需要自定义一个空的private构造函数:
class Person private constructor(){
}
抽象类
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
嵌套类
class Outer { // 外部类
private val bar: Int = 1
class Nested { // 嵌套类
fun foo() = 2
}
}
fun main(args: Array<String>) {
val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo) // == 2
}
内部类
关键字inner
内部类会带有一个对外部类的引用,要访问来自外部作用域的this,要使用this@label,label为外部类的类名
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
匿名内部类
使用对象表达式来创建匿名内部类
class Test {
var v = "成员属性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定义接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例。
*/
test.setInterFace(object : TestInterFace {//object是关键字,不能用别的词替代!!
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
}
类的修饰符
/*
classModifier: 类属性修饰符,标示类本身特性。
*/
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承,类默认是final的
annotation // 注解类
/*
accessModifier: 访问权限修饰符
*/
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见
嵌套类和内部类
创建时
var demo = Outter.Nested() //嵌套类,Outter后没有括号
var demo = Outter().Inner() //内部类,有括号
也就是说,要想构造内部类的对象,必须先构造外部类的对象,而嵌套类则不需要
引用外部类成员变量时
嵌套类内需要创建外部类实例再引用,内部类直接用this@
Any
Any是所有类的超类
class Example //从Any隐式继承
Any默认提供了三个函数
equals()
hashCode()
toString()
若一个类要被继承,可用open关键字进行修饰
open class Base(p:Int)
class Derived(p:Int):Base(p)
构造函数
若子类有主构造函数,则基类必须在主构造函数中立即初始化
open class Person(name:String)
class Student(name:String,id:String):Person(name)
子类无主构造函数,则必须在每一个二级构造函数中用super关键字初始化基类,或在代理另一个构造函数。
class Student:Person(){
constructor(name:String):super(name)
}
重写
重写方法
–final修饰的方法不能被子类重写
–允许子类重写的方法要用open修饰,子类重写方法时要用override
open class Person{
open fun f(){
}
}
class Student:Person(){
override fun f(){
print("xx")
}
}
若多个相同方法(继承或实现自不同父类,则必须重写该方法,使用super去选择性调用父类的实现
open class A(){
open fun f(){
print("I m A")
}
}
interface B(){
open fun f(){
print("I m B")
}
}
class C:A(),B{
override fun f(){
super<A>.f() //可不写
super<B>.f() //可不写
}
}
C可继承A、B中共有的函数,重写时必须(?)调用A、B中该函数的实现,并提供自己的实现
属性重写
属性必须具有兼容类型才能被重写,每一个声明的属性都可以通过初始化程序或getter方法被重写
open class Base{
open val x:Int get{...}
}
class Example:Base(){
override var x:Int = ...
}
可用一个var属性重写一个val属性,反之不行。
接口
- 一个类或对象可继承多个接口
- 接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性
interface MyInterface {
var name:String //name 属性, 抽象的
fun bar()
fun foo() {
// 可选的方法体
println("foo")
}
}
class Child : MyInterface {
override var name: String = "runoob" //重写属性
override fun bar() {
// 方法体
println("bar")
}
}
fun main(args: Array<String>) {
val c = Child()
c.foo();
c.bar();
println(c.name)
}
扩展
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
扩展函数
扩展函数可以在已有类中添加新的方法,不会对原类做修改。
fun receiverType.functionName(params){
...
}
/*
以下为实例
*/
class Example()
fun Example.Print(){
print("halo")
}
this关键字指代接收者对象(receiver object)(也就是调用扩展函数时, 在点号之前指定的对象实例)。
// 扩展函数 swap,调换不同位置的值
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this 对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
fun main(args: Array<String>) {
val l = mutableListOf(1, 2, 3)
// 位置 0 和 2 的值做了互换
l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值
println(l.toString())
}
若成员函数和扩展函数一致,优先使用成员函数
class C {
fun foo() { println("成员函数") }
}
fun C.foo() { println("扩展函数") }
fun main(arg:Array<String>){
var c = C()
c.foo() //输出结果为 成员函数
}
扩展空对象
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。
fun Any?.toString(): String {
if (this == null) return "null"
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
// 解析为 Any 类的成员函数
return toString()
}
fun main(arg:Array<String>){
var t = null
println(t.toString())
}
– 扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。
– 扩展属性不允许被初始化,只能通过显式提供的setter/getter定义
– 扩展属性只能声明为val
val Example.age = 1 //错误
伴生对象的扩展
伴生对象通过"类名."形式调用伴生对象,伴生对象声明的扩展函数,通过用类名限定符来调用
伴生对象内的成员相当于Java 中的静态成员
class MyClass {
companion object { } // 将被称为 "Companion"
}
fun MyClass.Companion.foo() {
println("伴随对象的扩展函数")
}
val MyClass.Companion.no: Int
get() = 10
fun main(args: Array<String>) {
println("no:${MyClass.no}")
MyClass.foo()
}
扩展声明为成员
在一个类内部你可以为另一个类声明扩展。
分发接收者:扩展方法定义所在类的实例
扩展接收者:扩展方法的目标类型的实例
open class D {
}
class D1 : D() {
}
open class C {
open fun D.foo() {
println("D.foo in C")
}
open fun D1.foo() {
println("D1.foo in C")
}
fun caller(d: D) {
d.foo() // 调用扩展函数
}
}
class C1 : C() {
override fun D.foo() {
println("D.foo in C1")
}
override fun D1.foo() {
println("D1.foo in C1")
}
}
fun main(args: Array<String>) {
C().caller(D()) // 输出 "D.foo in C"
C1().caller(D()) // 输出 "D.foo in C1" —— 分发接收者虚拟解析
C().caller(D1()) // 输出 "D.foo in C" —— 扩展接收者静态解析
}
假如在调用某一个函数,而该函数在分发接受者和扩展接受者均存在,则以扩展接收者优先,要引用分发接收者的成员你可以使用限定的 this 语法。