- 类的基本概念
- 用class关键字标识,加open修饰符表示可继承,实例化无需new
val p = Person() - 可见修饰符:默认是public对所有类可见,protected关键字表示对当前类以及子类可见,private表示只对当前类可见,最后Kotlin新增了一个internal修饰符表示只对同一个模块中的类可见
- 类主要包含:构造函数和初始化块,函数,属性,嵌套类和内部类,对象声明
- 用class关键字标识,加open修饰符表示可继承,实例化无需new
构造函数
Kotlin中的构造函数分为两种:主构造函数和次构造函数,每个类都会有一个默认的不带参数的主构造函数,当然也可以显示的指明参数
- 主构造函数:
可以用constructor关键字标识,class Person constructor(name: String) {}, 如果主构造函数没有注释或者可见性修饰符可以省略这个关键字 class Person (name: String) {}。主构造函数没有函数体,需要一些初始化的工作可以在init {} 代码块中进行。
- 次构造函数:
一个类可以有多个次构造函数,但是必须要直接或者间接调用主构造函数,如下代码所示
class Student (name: String, age: Int, gender: String, val grade):Person(name, age, gender) {
constructor(name: String, age: Int, gender: String): this(name,age,gender,3){}
constructor():this("Bob",20,"male")
}
//类没有显示定义主构造函数只有次构造函数时的写法
class Student:Person {
constructor(name:String, age:Int) : super(name, age) {}
}
继承及重写
任何Kotlin的类都是有一个公共的父类Any,然后Any有三个方法equals(), hashCode(), toString(), 一个类默认是final不可继承的,如果想要它可继承就要加open关键字。同时如果父类有主构造函数,那么子类必须对其进行实现。如果父类没有主构造函数,那么就要在每个次构造函数中用super关键字对基类的进行实现如下代码所示:
class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) }可以对方法进行重写如下代码所示,但是被重写的方法要被open修饰,且所在的类不能是final类型的 ```kotlin open class Shape { open fun draw() { /…/ } fun fill() { /…/ } }
class Circle() : Shape() { override fun draw() { /…/ } }
//一个方法如果是被override修饰过了就默认为它是可被重写的,如果不想这样 //可以在前面加上final修饰符 open class Rectangle() : Shape() { final override fun draw() {/…/} }
- 可以对属性进行重写,如下所示
```kotlin
open class Shape {
open val count: Int = 0
}
class Rectangle : Shape() {
override val count = 45
}
也可以把val值重写为var但是要额外实现set方法,如下
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices
class Polygon : Shape {
override var vertexCount: Int = 0 // Can be set to any number later
}
派生类初始化顺序: 父类的构造函数执行后子类声明的属性才初始化,所以要注意尽量不要在基类中使用open members ```kotlin open class Base(val name: String) {
init { println(“Initializing a base class”) }
open val size: Int = name.length.also { println(“Initializing size in the base class: $it”) } }
class Derived( name: String, val lastName: String, ) : Base(name.replaceFirstChar { it.uppercase() }.also { println(“Argument for the base class: $it”) }) {
init { println("Initializing a derived class") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
}
fun main() { println(“Constructing the derived class(\”hello\”, \”world\”)”) Derived(“hello”, “world”) }
- **属性**
- 一个完整的属性声明格式如下,
```kotlin
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
//val 没有set方法,完整格式如下
class Rectangle(val width: Int, val height: Int) {
val square: Int
get() = this.width * this.height
}
//var
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value)
}
//调用
open class Person {
var age: Int = 10
//getter缺省为默认
//setter设置参数前打印参数
set(value) {
println("setter $value")
//field关键字指向属性本身
field = value
}
}
@JvmStatic
fun main(args: Array<String>) {
val p = Person()
println(p.age)
p.age = 30
println(p.age)
}
幕后字段,field 标识符只能用在属性的访问器内。
var counter = 0 // the initializer assigns the backing field directly set(value) { if (value >= 0) field = value // counter = value // ERROR StackOverflow: Using actual name 'counter' would make setter recursive }延迟初始化, 加上lateinit关键字, 用isInitialized来判断是否已经初始化了
- 接口
Kotlin的接口和JAVA类似,一个类可以实现多个接口,接口中可以定义属性,但是必须是抽象的或者提供了访问器实现。接口中的属性没有backing field, 所以接口中声明的访问器不能引用它们 ```kotlin interface MyInterface { val prop: Int // 抽象的
val propertyWithImplementation: String get() = “foo”
fun foo() { print(prop) } }
class Child : MyInterface { override val prop: Int = 29 }
- 接口是可以继承的,所以它既可以实现父接口也可以定义新的属性
```kotlin
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String get() = "$firstName $lastName"
}
data class Employee(
// implementing 'name' is not required
override val firstName: String,
override val lastName: String,
val position: Position
) : Person
接口重写冲突:
interface A { fun foo() { print("A") } fun bar() } interface B { fun foo() { print("B") } fun bar() { print("bar") } } class C : A { override fun bar() { print("bar") } } class D : A, B { override fun foo() { super<A>.foo() super<B>.foo() } override fun bar() { super<B>.bar() } }
拓展函数和拓展属性
声明一个拓展函数时要有一个被拓展类型作为前缀,不过可以泛化它。拓展并没有修改所拓展的类,也没有插入新的成员,仅仅是可以用点表达式来调用而已,
fun <T> MutableList<T>.swap(index1: Int, index2: Int) { val tmp = this[index1] // 'this' corresponds to the list this[index1] = this[index2] this[index2] = tmp }拓展是静态的,如下代码会输出Shape。如果和成员函数有冲突则成员函数的优先级更高 ```kotlin open class Shape class Rectangle: Shape()
fun Shape.getName() = “Shape” fun Rectangle.getName() = “Rectangle”
fun printClassName(s: Shape) { println(s.getName()) }
printClassName(Rectangle())
- 拓展属性由于没有实际的将成员插入类中,幕后字段是无效的,所以不能有初始化容器只能显示定义setters和 getters,如下代码
```kotlin
val <T> List<T>.lastIndex:Int
get() = size - 1
//下面代码会报错
val House.number = 1
- 对于分发接收者与扩展接收者的成员名字冲突的情况,扩展接收者优先。引用分发接收者的成员要用this。
class Connection { fun Host.getConnectionString() { toString() // 调用 Host.toString() this@Connection.toString() // 调用 Connection.toString() } }
泛型
Kotlin中也有泛型的概念,但是如果可以推断的出来就可以省略类型参数
class Box<T>(t:T) { var value = t } //带类型参数的 val box:Box<Int> = Box<Int>(1) //不带类型参数 val box = Box(1)Kotlin中的泛型可以加In和Out修饰符,当一个类 C 的类型参数 T 被声明为 out 时它只能被消费,而In只能用于生产,这样可以防止一些类型安全的问题。下面是out和in的示例: ```kotlin //泛型接口 Source
中不存在任何以 T 作为参数的方法,只是返回 T 类型值 interface Source { T nextT(); } //在 Source
