美文网首页
Kotlin基础2

Kotlin基础2

作者: isLJli | 来源:发表于2020-06-27 11:24 被阅读0次

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

  1. 虽然伴生对象的成员看起来向java中的静态成员,但在运行期,依旧是真实对象的实例成员。
  2. 如果不提供伴生对象名字,那么编译器会提供一个默认的名字Companion。
  3. 通过@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个条件:

  1. lateinit只能用在类体中声明的Var属性,不能用在primary constructor声明的属性上
  2. 属性不能拥有自定的setter和getter方法
  3. 属性类型需要为非空,且不能是原生类型
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的四种可见性修饰符

  1. 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的扩展函数

  1. 扩展本身并不会真正修改目标类,也就是说它并不会在目标类中插入新的属性或方法
  2. 扩展函数的解析是静态分发的,而不是动态的,即不支持多态,调用只取决于对象的声明类型
  3. 调用是由对象声明类型所决定的,而不是由对象的实际类型决定
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

  1. 如果扩展函数与类中的方法重名,则调用类中的方法
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)
}

数据类

数据类需求满足如下要求:

  1. 主构造方法至少要有一个参数
  2. 所有的主构造方法参数都需要标记为var或时val
  3. 数据类不能时抽象的、open的、sealed的以及inner的

对于数据类来说,编译器会自动生成如下内容:

  1. equals/hashCode
  2. toString()方法,形式为Person(name=..,)
  3. 针对属性的componentN方法,并且按照属性声明顺序来生成

对于数据类成员的继承要点

  1. 如果数据类中显示定义了equals,hashCode或是toString方法,或是在数据类的父类中将这些方法声明为final,
    那么这戏方法就不会再生成,转而使用已有的。

  2. 如果父类拥有componentN方法,并且是open的以及返回兼容的类型,那么编译器就会在数据类中生成相应的componentN方法并且覆盖,
    如果父类中的这些方法由于不兼容的签名或是被定义为Final,那么编译器会报错

  3. 在数据类中显示提供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()))
}

相关文章

网友评论

      本文标题:Kotlin基础2

      本文链接:https://www.haomeiwen.com/subject/ebiiahtx.html