类和对象
创建类:
class Person constructor(name: String) {
var name: String
get() = field.toUpperCase()
set(value) {
if (value == null) {
field = ""
} else {
field = value
}
}
}
使用:
val person = Person("name")
person.name = "hello" //这里实际上调用的是person.setName(hello)
print(person.name) //这里实际上调用的是person.getName()方法,所以打印结果是HELLO
这里变量的set get方法中用有两个参数,filed表示当前的成员变量,value表示set方法传如的参数,这样设计的原因是,如果set方法中使用的是name = xxx,那么这句赋值又会调用自身的set方法,从而导致递归的死循环;
另外根据定义,val类型的变量不能有set方法
次构造器:
class Person(name: String) {
//所有的次构造器都要代理主构造器
constructor(name: String, test: Test) : this(name) {
this.test= test;
}
var name = name
//可以使用lateinit在次构造器或init中延迟加载,但它只能修饰非基本类型
lateinit var test :Test
//如果是基本类型,而主构造器中又没有该字段,只能声明为Int?并临时赋值
var age :Int? = null
}
嵌套类:
嵌套类类似与java的静态内部类,构造嵌套类不需要外部类的实例;
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
}
内部类:
内部类同java的内部类,要想构造内部类必须构造外部类的实例,
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() {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
匿名内部类:
test.setInterFace(object : TestInterFace {
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
类修饰符:
//类属性修饰符
abstract // 抽象类
open // 类可继承,类默认是final的
final // 类不可继承,默认属性
enum // 枚举类
annotation // 注解类
访问权限修饰符
private
protected
public
internal // 同一个模块中可见
继承和接口
所有类都隐式继承Any,Any提供了三个函数:
equals()
hashCode()
toString()
继承:
要想被继承必须要被open修饰,子类必须要继承父类的某个构造器,即必须直接或间接继承主构造器:
同样的,想要被重写的函数也必须要用open修饰
open class Person(name: String) {
var name = name
open var age: Int? = null
constructor(name: String, age: Int) : this(name) {
this.age = age
}
open fun sleep() {
print("睡觉")
}
}
class Student(name: String, age: Int) : Person(name) {
//重写age变量,重写变量的数据类型不能改变,但是可以用var重写val变量
override var age = age
var id: String? = null
//重写方法
override fun sleep() {
print("上课睡觉")
}
}
同Java类似,Kotlin不支持多继承
接口:
继承类和实现接口的区别是,继承类要继承其构造方法(就是后边带括号),实现接口后边没有括号和参数
interface MyInterface {
var name:String //name 属性是抽象的
fun fun1() //未实现
fun fun2() { //可以实现默认方法
println("fun2")
}
}
//实现接口
class Person: MyInterface {
override var name: String = "Person" //重写属性
override fun fun1() {
// 方法体
}
}
由于接口可以有默认实现,所以需要在继承多个接口时区分不同接口的同名函数:
interface A {
fun fun1() { print("fun1") }
fun fun2()
}
interface B {
fun fun1() { print("B") }
fun fun2()
}
class C : A, B {
override fun foo() {
super<A>.fun1()
super<B>.fun1()
}
override fun fun2() {
super<B>.fun2()
}
}
函数扩展:
class Person(var name:String)
//成员函数和扩展函数同名时优先使用成员函数
//扩展函数不需要写在类中,直接在kotlin文件中定义一个方法就好了
fun Person.Print(){
print("用户名 $name")
}
fun main() {
var person = Person("Tom")
person.Print()
}
伴生对象:
class Student(name: String, age: Int?) : Person(name) {
companion object { //object后面可以加类名,一般省略
var name = "Tom"
fun getName(): String{
return name
}
}
}
fun main() {
print(Studnet.name);
print(Studnet.getName);
}
一个类中只能声明一个companion对象,companion对象类似于Java中的static对象
数据类
用data关键字声明数据类:
data class Person(val name: String, val age: Int)
数据
1.主构造函数至少包含一个参数。
2.所有的主构造函数的参数必须标识为val 或者 var ;
3.数据类不可以声明为 abstract, open, sealed 或者 inner;
4.数据类不能继承其他类 ,但是可以实现接口
对象属性修改:
data class Person(val name: String, val age: Int)
fun main() {
val p1 = Person("Tom", 12)
val p2 = p1.copy(name = "Bob")
print(p1.toString()) //Person(name=Tom, age=12)
print(p2.toString()) //Person(name=Bob, age=12)
person.name = "ppp" //val类型的属性不能修改它的内容,除非生命为var
}
密封类
泛型
class Box<T>(t: T) {
var value = t
}
fun(){
val box1 = boxIn<Int>(1)
val box2 = boxIn(1) //自动推断类型
}
泛型函数:
fun <T> print(content: T) {
when (content) {
is Int -> println("Int")
is String -> println("String ")
else -> println("other")
}
}
泛型约束:
fun <T : Person> get() {
//限制T必须是Person的子类
}
协变(out)和逆变(in)
将泛型作为内部方法的返回,用 out:
interface Production<out T> {
fun produce(): T //T只能用作函数的返回,而不能用T作为函数参数
}
将泛型对象作为函数的参数,用 in:
interface Consumer<in T> {
fun consume(item: T) //T只能用作函数的参数,而不能返回T类型的对象
}
既将泛型作为函数参数,又将泛型作为函数的输出,那就既不用 in 或 out。
interface ProductionConsumer<T> {
fun produce(): T
fun consume(item: T)
}
枚举类:
简单枚举:
enum class Color {
RED, GREEN, BLUE
}
带参数:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
fun main(){
Color.RED.rgb
}
带匿名方法:
enum class Color {
RED {
override fun draw() {
print("red")
}
},
WHITE {
override fun draw() {
print("white")
}
};
abstract fun draw()
}
fun main(){
Color.RED.draw()
Color.WHITE .draw()
}
object:
fun main(args: Array<String>) {
val temp = object {
var name = "hello world"
}
println(site.name)
}
object还可以用来实现匿名内部类:
open class Student(var v: String?) {
var name: String? = null
}
var school = School(object : Student("Tom") {
var age = 12
})
object还可以用来实现单例:
object DataManager {
var data: IntArray? = null
}
//访问data直接用类名+变量名
DataManager.data
代理:
interface Base {
fun print()
}
class BaseImpl : Base {
override fun print() {
print("hello world")
}
}
//代理类声明
class BaseProxy(base: Base) : Base by base
fun main() {
BaseProxy(BaseImpl()).print()
}
属性代理:
class Person {
var id: String by PersonDelegate()
}
class PersonDelegate {
operator fun getValue(ref: Any?, property: KProperty<*>): String {
return (ref as Person).id //这样写会由于递归导致栈溢出
}
operator fun setValue(ref: Any?, property: KProperty<*>, value: String) {
//这里已经自动给ref赋值了,只要添加其他逻辑就好了
return
}
}
几种标准代理:
1.延迟初始化代理:
val id: String by lazy {
print("call set id")
"Hello"
}
lazy中的代码块只会在初始化时执行一次
2.观察者代理:
var id: String by Delegates.observable("init") { property, oldValue, newValue ->
print(property.name + " oldValue=" + oldValue + " newValue=" + newValue)
}
3.属性映射代理:
可以用一个map来初始化类:
class Person(map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(){
var p = Person(mapOf("name" to "Tom", "age" to 12))
print(p.name + p.age)
}
如果Person的成员变量类型是var,需要用MutableMap来初始化