kotlin的构造方法
在kotlin中,一个类可以有一个primary构造方法以及一个或多个secondary构造方
// 在kotlin中,一个类可以有一个primary构造方法以及一个或多个secondary构造方法
//primary构造方法,是类头,它位于类头的后面
// 如果primary构造方法没有任何注解或者可见性关键字修饰,那么constructor关键字可省略
class MyCalss constructor(userName:String){
private val username :String = userName.toUpperCase()
init {
println(userName)
}
}
fun main(args:Array<String>){
var myCalss = MyCalss("张三")
}
- primary构造方法,是类头,它位于类头的后面
- 如果primary构造方法没有任何注解或者可见性关键字修饰,那么constructor关键字可省略
打印结果:
张三
对于secondary构造方法必须直接或间接的调用primary构造方法
class Person constructor(userName:String){
private var userName:String? = null
private var age:Int
private var address:String
init {
println(userName)
this.userName = userName
this.age = 20
this.address = "guangdong"
}
//对于secondary构造方法必须直接或间接的调用primary构造方法
constructor(userName: String,age:Int):this(userName){
this.age = age
this.address = "guangdong"
}
constructor(userName: String,age: Int,address:String):this(userName,age){
this.address = address
}
fun printInfo(){
println("username = ${this.userName},age = ${this.age} , address = ${this.address}")
}
}
fun main(args:Array<String>){
val person = Person("zhangsan")
val person2 = Person("lisi",30)
val person3 = Person("wangwu",30,"hangzhou")
person.printInfo()
person2.printInfo()
person3.printInfo()
}
打印结果:
zhangsan
lisi
wangwu
username = zhangsan,age = 20 , address = guangdong
username = lisi,age = 30 , address = guangdong
username = wangwu,age = 30 , address = hangzhou
构造方法的参数可以当全局变量使用
class Student(private val userName:String , private val age:Int,private var address:String){
fun printInfo(){
println("userName= $userName, age = $age , address = $address")
}
}
fun main(args:Array<String>){
val student = Student("zhangsan",20,"shenzhen")
student.printInfo()
}
打印结果
userName= zhangsan, age = 20 , address = shenzhen
构造方法的都拥有默认值
/**
* 在JVM上,如果类的primary构造方法的所有都拥有默认值,那么kotlin编译器就会为这个生成一个不带参数的构造方法
* 在这个不带参数的构造方法会使用这些参数的默认值。
*/
class Student3 (val userName: String = "zhangsan"){
}
fun main(args:Array<String>){
var student3 = Student3()
println(student3.userName)
student3 = Student3("lisi")
println(student3.userName)
}
打印结果:
zhangsan
lisi
Kotlin的继承
- 在kotlin中,所有类和方法默认情况下都是final的,所有类在默认下都是无法被继承的
- open含义与final相反
//在kotlin中,所有类默认情况下都是final的,所有类在默认下都是无法被继承的
// open含义与final相反
open class Parent(name:String,age:Int){
}
class Child(name: String,age: Int):Parent(name,age){
}
- 在kotlin中,如果一个类没有primary构造方法,那么这个类的每个secondary构造方法就需要通过super关键字来初始化父构造。
open class Parent2(name: String){
}
class Child2:Parent2{
constructor(name: String):super(name){
}
}
- Open声明的类,其方法也要声明为open才能被子类重写。
open class Fruit{
open fun name(){
println("fruit")
}
fun expirationDate(){
println("1 month")
}
}
class Apple:Fruit(){
override fun name(){
println("apple")
}
}
open class Orange:Fruit(){
override fun name() {
println("orange")
}
}
fun main(args:Array<String>){
var apple = Apple()
apple.name()
apple.expirationDate()
}
打印结果:
apple
1 month
val的属性不能set
- 1.val 可以 override val
- 2.var 可以 override val
- 3.val 无法 override var
- 本质上,val相当于get方法,var相当于get与set方法
open class MyParent3{
open fun method(){
println("parent method")
}
open val name:String get() = "parent"
}
class MyChild3:MyParent3(){
override fun method() {
super.method()
println("child method")
}
override val name: String
get() = super.name+ " and child"
}
fun main(args:Array<String>) {
var myChild3 = MyChild3()
myChild3.method()
println(myChild3.name)
}
打印结果:
parent method
child method
parent and child
继承和实现的类有相同的方法
要么自己重写方法,否则要指明表示那个类的方法。接口有自己的默认方法实现
//接口默认实现方法
interface A{
fun method(){
println("A")
}
}
//开放类实现方法
open class B {
open fun method(){
println("B")
}
}
//继承类和实现类有同一个方法
class C:B(),A{
override fun method() {
println("C")
}
}
//用接口A类的方法
class D:B(),A{
override fun method() {
super<A>.method()
}
}
class E:B(),A{
override fun method() {
super<A>.method()
super<B>.method()
}
}
fun main(args:Array<String>){
val c =C()
c.method()
val d = D()
d.method()
val e = E()
e.method()
}
打印结果:
C
A
A
B
伴生对象companion object
- 虽然伴生对象的成员看起来向java中的静态成员,但在运行期,依旧是真实对象的实例成员。
- 如果不提供伴生对象名字,那么编译器会提供一个默认的名字Companion。
- 通过@JvmStatic注解可实现的伴生对象在编译后会生成一个静态内部类、方法、属性。
// object declaration, 对象声明
object MyObject{
fun method(){
println("method")
}
}
fun main(args:Array<String>){
MyObject.method()
println("----------")
MyTest.MyObject.method()
println("----------")
//类似于静态方法,Kotlin中没有静态方法
println(MyTest.a)
println(MyTest.method())
println("----------")
println(MyTest2.Companion.a)
println(MyTest2.a)
}
//companion object,伴生对象
//在Kotlin中,与Java不同的是,类是没有static方法的
//在大多数情况下,kotlin推荐的做法是使用包级别的函数作为静态方法
// kotlin会将包级别的函数当作静态方法来看待
//注意:虽然伴生对象的成员看起来向java中的静态成员,但在运行期,依旧是真实对象的实例成员
//在JVM上,可以将伴生对象的成员真正生成为类的静态方法与属性,这是通过@JvmStatic注解来实现的
//伴生对象在编译后会生成一个静态内部类
class MyTest{
//类似于静态方法
companion object MyObject{
var a:Int = 1000
fun method(){
println("methiod invoked!")
}
}
}
class MyTest2{
//如果不提供名字,那么编译器会提供一个默认的名字Companion
companion object {
var a:Int = 1000
fun method(){
println("methiod invoked!")
}
}
}
打印结果:
method
methiod invoked!
1000
methiod invoked!
kotlin.Unit
1000
1000
属性的get/set方法
class ThePerson(address:String,name:String){
val age:Int
get() = 20
var address:String = address
get() {
println("get invoked")
return field
}
set(value) {
println("set invoked")
field = value
}
var name :String = name
}
fun main(args:Array<String>){
var person = ThePerson("huizhou","zhangsan")
println(person.age)
println("********")
println(person.address)
person.address = "gz"
println(person.address)
println("********")
println(person.name)
person.name ="lisi"
println(person.name)
}
打印结果:
20
get invoked
huizhou
set invoked
get invoked
gz
zhangsan
lisi
属性延迟初始化
通过lateinit关键字标识属性为延迟初始化,需要满足3个条件:
- lateinit只能用在类体中声明的Var属性,不能用在primary constructor声明的属性上
- 属性不能拥有自定的setter和getter方法
- 属性类型需要为非空,且不能是原生类型
class TheClass{
lateinit var name:String
fun init(){
this.name ="zhangsan"
}
fun print(){
println(this.name)
}
}
fun main(args:Array<String>){
var theClass = TheClass()
theClass.init()
theClass.print()
}
打印结果:
zhangsan
kotlin的四种可见性修饰符
- kotlin提供了四种可见性修饰符:private、protected、internal、public ; internal:模块化范围 ;kotlin默认为public
// 可见性 visibility
// kotlin提供了四种可见性修饰符:private、protected、internal、public
// internal:模块化范围
//kotlin默认为public
open class Clazz{
private val b = 3
protected open val c = 4
internal val d = 5
val e =6
}
class subClazz:Clazz(){
fun p(){
println(c)
println(d)
println(e)
}
}
class Claazz2{
var clazz = Clazz()
fun p(){
println(clazz.d)
println(clazz.e)
}
}
Kotlin的扩展函数
- 扩展本身并不会真正修改目标类,也就是说它并不会在目标类中插入新的属性或方法
- 扩展函数的解析是静态分发的,而不是动态的,即不支持多态,调用只取决于对象的声明类型
- 调用是由对象声明类型所决定的,而不是由对象的实际类型决定
class ExtensionTest{
fun add(a:Int,b:Int) = a+b
fun subtract(a:Int,b:Int) = a-b
}
//为ExtensionTest类扩展了一个新方法,扩展函数
fun ExtensionTest.multiply(a:Int,b:Int) = a+b
fun main(args:Array<String>){
val extensionTest = ExtensionTest()
println(extensionTest.add(1,2))
println(extensionTest.subtract(1,2))
println(extensionTest.multiply(1,2))
println("********")
mtPrint(AA())
mtPrint(BB())
}
//扩展函数的解析是静态的
/**
* 1.扩展本身并不会真正修改目标类,也就是说它并不会在目标类中插入新的属性或方法
* 2.扩展函数的解析是静态分发的,而不是动态的,即不支持多态,调用只取决于对象的声明类型
* 3.调用是由对象声明类型所决定的,而不是由对象的实际类型决定
*/
open class AA
class BB:AA()
fun AA.a() = "a"
fun BB.a() = "b"
fun mtPrint(aa:AA){
println(aa.a())//只调用AA.a()方法
}
打印结果:
3
-1
3
a
a
- 如果扩展函数与类中的方法重名,则调用类中的方法
class CC{
fun foo(){
println("number")
}
}
// 如果扩展函数与类中的方法重名,则调用类中的方法
fun CC.foo(){
println("number2")
}
fun CC.foo(int: Int){
println("number3")
}
fun main(args:Array<String>){
CC().foo()
CC().foo(1)
}
打印结果:
number
number3
扩展属性
class MyExtensionProperty
//扩展属性
val MyExtensionProperty.name:String
get() = "hello"
fun main(args:Array<String>){
var myExtensionProperty = MyExtensionProperty()
println(myExtensionProperty.name)
}
数据类
数据类需求满足如下要求:
- 主构造方法至少要有一个参数
- 所有的主构造方法参数都需要标记为var或时val
- 数据类不能时抽象的、open的、sealed的以及inner的
对于数据类来说,编译器会自动生成如下内容:
- equals/hashCode
- toString()方法,形式为Person(name=..,)
- 针对属性的componentN方法,并且按照属性声明顺序来生成
对于数据类成员的继承要点
-
如果数据类中显示定义了equals,hashCode或是toString方法,或是在数据类的父类中将这些方法声明为final,
那么这戏方法就不会再生成,转而使用已有的。 -
如果父类拥有componentN方法,并且是open的以及返回兼容的类型,那么编译器就会在数据类中生成相应的componentN方法并且覆盖,
如果父类中的这些方法由于不兼容的签名或是被定义为Final,那么编译器会报错 -
在数据类中显示提供componentN方法以及copy方法实现是不允许的
解构声明
在主构造方法中有多少个参数,就会依次生成对应的component1,component2,component3...
在这些方法返回的就是对应字段的值,componentN方法是用来实现解构声明的
data class Person(val name:String,var age:Int,var address:String)
fun main(args:Array<String>){
var person = Person("zhangsan",20,"beijing")
println(person)
person.age = 30
println(person.age)
//复制对象,并改其地址
var person2 = person.copy(address = "guangdong")
println(person2)
//解构
var(name,age,address) = person
println("$name,$age,$address")
}
结果:
Person(name=zhangsan, age=20, address=beijing)
30
Person(name=zhangsan, age=30, address=guangdong)
zhangsan,30,beijing
密封类
密封类(sealed class) ,非常像java的枚举
表示一种受限的层次结构(子父类),本身是一个抽象的类。常用在when中
/*
密封类(sealed class) ,非常像java的枚举
表示一种受限的层次结构(子父类),本身是一个抽象的类
*/
//密封类
sealed class Calculator
//密封类的子类
class Add:Calculator()
//密封类的子类
class Subtract:Calculator()
//密封类的子类
class Multiply:Calculator()
//密封类在when中使用最多
fun calculate(a:Int,b:Int,cal:Calculator) = when (cal){
is Add -> a+b
is Subtract -> a-b
is Multiply -> a*b
}
fun main(args:Array<String>){
println(calculate(1,2,Add()))
println(calculate(1,2,Subtract()))
println(calculate(1,2,Multiply()))
}
网友评论