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的子类,这么做是为了限制类的扩展,防止可能存在的安全隐患
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值