Kotlin学习笔记之面向对象

Posted mictoy_朱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin学习笔记之面向对象相关的知识,希望对你有一定的参考价值。

面向对象

接口定义

与java一样,使用interface表示,示例代码:
与java一样,kotlin定义类时要遵循单继承多实现的原则(即只能继承一个父类,可以实现多个接口)
kotlin中定义的类和方法默认都是final的,不可重写,如果要实现重写,需将对应方法类声明为open
示例代码:

package com.zhusp.kotlinuse

open class Person		//可继承的类必须要用open关键字修饰
    open fun work()		//同样,里面的方法也需要用open修饰后才能在子类中重写,相当于java中的public
    
    


interface Driver 
    fun drive()


interface Writer 
    fun write()


class Manager(var driver:Driver,var writer: Writer):Driver by driver,Writer by writer   //by 表示代理,此时无需再重写实现接口的方法

class Professor : Person()
    override fun work()

    


class CarDriver : Driver 
    override fun drive() 
        println("轿车司机正在开车来")
    


class PPTWriter : Writer 
    override fun write() 
        println("秘书正在写PPT")
    


fun main() 
    val carDriver = CarDriver()
    val pptWriter = PPTWriter()
    val seniorManager = Manager(carDriver,pptWriter)

    seniorManager.drive()
    seniorManager.write()

运行结果

轿车司机正在开车来
秘书正在写PPT

object

object在kotlin中同class关键字一样,是表示一个类的,与普通class修饰的类不同的是,由object定义的类只有一个实例,并且不嫩自定义构造方法,其实它的本质就是java中对类的单例化。
示例代码:

interface MediaPlay
    fun mount()
    fun unMount()


abstract class Player
    abstract fun deviceName()
    abstract fun deviceNum()


object MusicPlayer:Player(),MediaPlay
    override fun mount() 

    

    override fun unMount() 

    

    override fun deviceName() 
        println("I am MusicPlayer")
    

    override fun deviceNum() 

    


上面定义的MusicPlayer在其它kotlin可以直接通过该类名调用其中的方法:

MusicPlayer.deviceName()

运行:

I am MusicPlayer

object小结
  • 只有一个实例的类
  • 不能自定义构造方法
  • 可以实现接口,继承父类
  • 本质上就是单例最基本的实现

伴生对象与静态成员(public static void main)

  • kotlin中每个类可以对应一个伴生对象
  • 伴生对象的成员全局独一份
  • 伴生对象的成员类似Java的静态成员
  • 伴生对象用companion object表示

kotlin伴生对象代码示例:

fun main()     //直接调用
    println(StaticDemo.TAG)
    println(StaticDemo.ofDouble(3.0))


class StaticDemo private constructor(val value:Double)
    companion object //表示伴生对象,类似于java中对方法或变量声明static
        @JvmStatic
        fun ofDouble(double:Double):StaticDemo
            return StaticDemo(double)
        
        @JvmField
        val TAG:String = "StaticDemo"
    

  • 静态成员考虑用包级函数、变量替代
  • JvmField和JvmStatic的使用(保证在java里调用时看起来跟普通java类的静态方法或变量一致)

扩展成员(二次加工)

类似于我们在Java中定义工具类及工具方法,不一样的是kotlin可以在原本存在的类(jdk或jar中的类,比如String)里直接扩展方法(就是所谓的二次加工)实现我们想要的功能的工具类方法,比如,现在需要一个循环字符串的工具方法(String本身没有这样的方法):

fun String.multiply(int :Int):String//扩展String里的方法
    val strBuilder = StringBuilder()
    for (i in 0 until int)
        strBuilder.append(this)//这里this代表String
    
    return strBuilder.toString()


fun main() 
    println("hello".multiply(6))//此时 就可以直接通过String调用multiply方法(实现二次加工)

运行结果:

hello,hello,hello,hello,hello,hello,
除了扩展方法,kotlin还可以扩展成员变量,需要注意的是,扩展变量时,需要用get()方法来设定默认值,例如:

val String.version: String
    get() = "1.0.2"

这是,在使用String对象时就可以直接获取这个扩展变量

试一试:

println("Hello".version)

运行结果:

1.0.2

小结

  • 为现有类添加方法、属性

    — fun X.y():Z…
    — val X.m 注意扩展属性不能初始化 ,类似接口属性

  • Java调用扩展成员类似调用静态方法

数据类

  • 解放Java中写JavaBean时大量代码的麻烦里
  • 默认实现copy、toString等方法
  • 生成对应属性个数的componentN方法
  • 需添加allOpen和noArg插件(填坑插件)

示例代码:
kotlin实现数据类:

data class Country(val id:Int,val name:String)

编译后自动生成的对应的java代码:

public final class Country 
   private final int id;
   @NotNull
   private final String name;

   public final int getId() 
      return this.id;
   

   @NotNull
   public final String getName() 
      return this.name;
   

   public Country(int id, @NotNull String name) 
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.id = id;
      this.name = name;
   

   public final int component1() 
      return this.id;
   

   @NotNull
   public final String component2() 
      return this.name;
   

   @NotNull
   public final Country copy(int id, @NotNull String name) 
      Intrinsics.checkParameterIsNotNull(name, "name");
      return new Country(id, name);
   

   // $FF: synthetic method
   @NotNull
   public static Country copy$default(Country var0, int var1, String var2, int var3, Object var4) 
      if ((var3 & 1) != 0) 
         var1 = var0.id;
      

      if ((var3 & 2) != 0) 
         var2 = var0.name;
      

      return var0.copy(var1, var2);
   

   @NotNull
   public String toString() 
      return "Country(id=" + this.id + ", name=" + this.name + ")";
   

   public int hashCode() 
      int var10000 = this.id * 31;
      String var10001 = this.name;
      return var10000 + (var10001 != null ? var10001.hashCode() : 0);
   

   public boolean equals(@Nullable Object var1) 
      if (this != var1) 
         if (var1 instanceof Country) 
            Country var2 = (Country)var1;
            if (this.id == var2.id && Intrinsics.areEqual(this.name, var2.name)) 
               return true;
            
         

         return false;
       else 
         return true;
      
   

从上面代码可知,kotlin中我们用一行代码就可以实现传统Java代码中繁杂JavaBean类的编写,这是kotlin给我们的便利之一
componentN是kotlin中除JavaBean中一般方法外特有的方法,使用示例:

fun main() 
    val china = Country(0,"China")
    println(china)
    println(china.component1())
    println(china.component2())
    println("--------------")
    val (id,name) = china
    println(id)
    println(name)

为方便理解上述代码,可看编译后对应生成的java代码:

public static final void main() 
      Country china = new Country(0, "China");
      System.out.println(china);
      int id = china.component1();
      System.out.println(id);
      String var9 = china.component2();
      System.out.println(var9);
      var9 = "--------------";
      System.out.println(var9);
      id = china.component1();
      String name = china.component2();
      System.out.println(id);
      System.out.println(name);
   

运行结果:

Country(id=0, name=China)
0
China
-------------
0
China


需要注意,kotlin数据类在没有添加allOpennoArg插件时生成的对应的JavaBean类是final类型的且没有无参构造函数,这是kotlin存在的问题,只有在集成了allOpennoArg插件后才会在对应生成的Java类中实现无参构造函数且为非final类,我们才能像使用普通JavaBean类那样使用我们需要的数据类,不过好像还是有坑,其成员变量还是final型的,并且没有对应的setter方法,只有getter方法,因此可能使用时还会有所限制

内部类

  • 定义在类内部的类
  • 与类成员有相似的访问控制
  • 默认是静态内部类,非静态用inner关键字
  • 使用this@Outter,this@Inner关键字来定位是外部类变量或内部类变量
匿名内部类
  • 没有定义名字的内部类
  • 类名编译时生成,类似Outter$1.class
  • 可继承父类、实现多接口,与Java注意区别

示例代码:

class Outter
    val a:Int = 4
    class Inner1   //默认为静态内部类
        fun getOutterA():Int
//            return a    //错误,编译不通,静态内部类,无法访问外部类变量
            return 0
        
    
    
    inner class Inner2 //设为非静态内部类
        val a:Int = 5
        fun getOutterA():Int
            return this@Outter.a    //可以访问外部变量a,若内部内中有同名变量,访问外部变量需用this@Outter
        
        
        fun getInnerA():Int
//            return this@Inner2.a  //获取内部变量a,可用this@Inner2
            return a    //也可以直接返回内部变量名
        
    

静态内部类与非静态内部类方法调用示例代码:

fun main() 
    val mOutter = Outter()
    println(mOutter.Inner2().getInnerA())//非静态内部类方法调用,需先new外部类实例
    println(Outter.Inner1().getOutterA())//静态内部类方法调用,直接使用外部类类名
    println(mOutter.Inner2().getOutterA())

枚举(实例可数)

  • 实例可数的类,注意枚举也是类
  • 可以修改构造,添加成员
  • 可以提升代码的表现力,也有一定的性能开销

示例代码:

enum class LogLevel(val id:Int)
    VERBOSE(0),DEBUG(1),INFO(2),WARN(3),ERROR(4),ASSERT(5);

    fun getTag(): String 
        return "$id->$name"
    

    override fun toString(): String 
        return "$id->$name"
    


fun main() 
    println(LogLevel.DEBUG.getTag())
    println(LogLevel.valueOf("ERROR"))

可查看其生成对应java代码:

public enum LogLevel 
   VERBOSE,
   DEBUG,
   INFO,
   WARN,
   ERROR,
   ASSERT;

   private final int id;

   @NotNull
   public final String getTag() 
      return this.id + "->" + this.name();
   

   @NotNull
   public String toString() 
      return this.id + "->" + this.name();
   

   public final int getId() 
      return this.id;
   

   private LogLevel(int id) 
      this.id = id;
   

// EnumUseKt.java
package com.zhusp.kotlinuse.dataclass;

public final class EnumUseKt 
   public static final void main() 
      String var0 = LogLevel.DEBUG.getTag();
      System.out.println(var0);
      LogLevel var1 = LogLevel.valueOf("ERROR");
      System.out.println(var1);
   

   // $FF: synthetic method
   public static void main(String[] var0) 
      main();
   

运行结果:

1->DEBUG
4->ERROR

密封类(子类数量有限的类)

子类可数
  • <v1.1,子类必须定义为密封类的内部类
  • =v1.1,子类只需要与密封类在同一个文件中即可

与枚举不同,枚举是其实例有限,而密封类是子类数量有限

示例代码
密封类关键字:sealed

sealed class PlayerCMD
    class PlayCMD(url:String,position: Long = 0):PlayerCMD()

    class SeekCMD(position:Long):PlayerCMD()

    object PauseCMD:PlayerCMD()

    object StopCMD:PlayerCMD()
    //不可在当前文件以外定义其它PlayerCMD的子类,这么做是为了限制类的扩展,防止可能存在的安全隐患

以上是关于Kotlin学习笔记之面向对象的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin学习笔记-----面向对象

kotlin学习笔记之闭包

kotlin学习笔记之闭包

kotlin 实战之面向对象特性全方位总结

Kotlin学习笔记-----接口

java学习笔记之面向对象