Kotlin基础(6)-类的种类

前言

本文主要介绍一下kotlin中的类种类。

嵌套类和内部类

kotlin的嵌套类和内部类在写法上只有inner关键字的区别,嵌套类直接在类中声明即可,内部类需要加上inner关键字

class Outer{

    var x:Int=10;

    class Inner{
         fun  foo(){
           //嵌套类不持有外部类引用
           // println(x)
        }
    }

}

class Outer1{
    var x:Int=0;
    inner class Inner{
        fun  foo(){
            //内部类持有外部类的引用
            println(x)
        }
    }
}

如果是嵌套类是不能持有外部类的引用,如果是内部类则可以持有外部类的引用。这是因为嵌套类是一个静态内部类,他不能持有外部类的引用。
java字节码如下

public final class Outer {
   private int x = 10;

   public final int getX() {
      return this.x;
   }

   public final void setX(int var1) {
      this.x = var1;
   }

   @Metadata(
      mv = {1, 1, 10},
      bv = {1, 0, 2},
      k = 1,
      d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002¨\u0006\u0003"},
      d2 = {"Lcom/zhqy/javademo/Outer$Inner;", "", "()V", "production sources for module app"}
   )
   public static final class Inner {
   }
}

数据类

在java中我们往往会将json字符串解析成一个个的java的类来保存数据,这些类称为实体类,kotlin中也为我们准备了类似的类结构,称为数据类。数据类的写法如下

data class Person(var name:String,var age:Int){

    var num:Int=0
   

}

Java字节码

public final class Person {
   private int num;
   @NotNull
   private String name;
   private int age;

   public final int getNum() {
      return this.num;
   }

   public final void setNum(int value) {
      this.num = value;
   }

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

   public final void setName(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.name = var1;
   }

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   public Person(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
      this.age = age;
   }

   @NotNull
   public final String component1() {
      return this.name;
   }

   public final int component2() {
      return this.age;
   }

   @NotNull
   public final Person copy(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      return new Person(name, age);
   }

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

      if((var3 & 2) != 0) {
         var2 = var0.age;
      }

      return var0.copy(var1, var2);
   }

   public String toString() {
      return "Person(name=" + this.name + ", age=" + this.age + ")";
   }

   public int hashCode() {
      return (this.name != null?this.name.hashCode():0) * 31 + this.age;
   }

   public boolean equals(Object var1) {
      if(this != var1) {
         if(var1 instanceof Person) {
            Person var2 = (Person)var1;
            if(Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}

从字节码中我们可以看到数据类为我们自动生成了很多函数和成员变量的get和set方法,当然我们也可以重写其中的函数和getset方法

data class Person(var name:String,var age:Int){

    var num:Int=0
    set(value) {
        field=value
    }
    get(){
        return field
    }


    override fun toString(): String {
        return ""
    }

}

数据类特点
(1)自动生成get/set方法
(2)自动创建component1-N(用于析构)
(3)自动创建toString/equals/hashCode

自动创建函数规则
(1)如果父类有toString/equals/hashCode方法且用final进行了修饰,子类则使用父类的相应方法
(2)如果父类正确实现了component,且使用open关键字进行修饰则子类可以重写该方法,否则报错
(3)不允许在类体中实现component和copy函数

Object

kotin中使用object替代Java中的匿名内部类来实现一个轻量级的对象。代码如下

interface  A{
    fun A()
}

interface  B{
    fun  B()
}

 val value: Any = object : A, B {
            override fun B() {
                println("B执行了")
            }

            override fun A() {
                println("A执行了")
            }

        }

可以看到kotlin中的object可以实现或继承对个接口或类

object充当匿名对象

object作为匿名对象只能作用于局部变量或私有声明

//局部变量
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val value= object : A, B {
            override fun B() {
                Log.e("B","执行了")
            }

            override fun A() {
                Log.e("A","执行了")
            }

        }
        value.A()
        value.B()
    }
class C{
    //类中私有声明
    private fun foo()=object {
        var x:Int=0;
    }

    fun printX(){
        println("${foo().x}")
    }

}

object可以修改闭包范围内的的变量

Java在匿名内部类中对外部变量需要用final进行修饰,并不能改变当前外部变量。而在kotlin中可以在object中对外部变量进行修改。

class MainActivity : AppCompatActivity() {

    open var x:Int=0;
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var value=object:A{
            override fun foo() {
                x++
                x++
            }

        }
        value.foo()
       Log.e("X","$x");

    }

    interface  A{
        fun foo()
    }
}

object充当单例类

在kotlin中使用object修饰的类充当单例类是一个非常不错的方式

 class Car{

}

object CarFactory{

    fun create():Car{
        return  Car()
    }
}

字节码如下

public final class CarFactory {
   
public static final CarFactory INSTANCE;

   @NotNull
   public final Car create() {
      return new Car();
   }

   static {
      CarFactory var0 = new CarFactory();
      INSTANCE = var0;
   }
}

可以看到在便以为Java字节码后,生成了一个变量INSTANCE,该变量在静态代码块中被赋值,且类的构造方法被private修饰,这意味着该类只有唯一的对象,该对象被INSTANCE引用。使用object充当单例类是线程安全的
需要注意的是如果想要在java使用该单例类需要这样使用

 CarFactory.INSTANCE.create()

或者在方法上打上 @JvmStatic,告知JVM虚拟机该函数是一个静态函数

伴随对象

伴随对象是一个特殊的对象类型,用于处理类似静态方法的问题

 class Car{

    companion object {
        fun create():Car{
            Log.e("Car","伴随对象中的函数执行了")
            return Car()
        }
    }

}

 //调用函数,类似于调用静态方法
 Car.create()

测试结果

11-04 11:57:16.343 25050-25050/com.zhqy.javademo E/Car: 伴随对象中的函数执行了

Java字节码

public final class Car {
   public static final Car.Companion Companion = new Car.Companion((DefaultConstructorMarker)null);

   @Metadata(
      mv = {1, 1, 10},
      bv = {1, 0, 2},
      k = 1,
      d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004¨\u0006\u0005"},
      d2 = {"Lcom/zhqy/javademo/Car$Companion;", "", "()V", "create", "Lcom/zhqy/javademo/Car;", "production sources for module app"}
   )
   public static final class Companion {
      @NotNull
      public final Car create() {
         Log.e("Car", "伴随对象中的函数执行了");
         return new Car();
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

从Java字节码中可以看出,伴随对象被解释成名为Companion内部静态类,如果Java代码需要调用的话,需要这样使用

Car.Companion.create

或者在伴随对象的方法上添加 @JvmStatic注解

 class Car{

    companion object {
        @JvmStatic
        fun create():Car{
            Log.e("Car","伴随对象中的函数执行了")
            return Car()
        }
    }

}

中缀调用(infix)

中缀表达式调用可以省略点和括号,通过空格的方式实现方法调用。中缀表达式是一种较为特殊的方法调用方式,需要满足以下几个条件:
(1)必须是成员函数或者是扩展函数
(2)必须仅有一个参数
(3)不能使用可变参数,且参数不能有默认值

class MainActivity : AppCompatActivity() {

    open var x:Int=0;
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val car=Car()
        //省略点以空格代替
        car foo(x)
    }

    class Car{

        infix fun foo(x:Int){
            Log.e("foo","$x")
        }

    }
}

测试结果如下

11-04 15:21:09.038 14872-14872/com.zhqy.javademo E/foo: 0

枚举

枚举主要是声明安全类型

 class GPS{
    var direction:Direction=Direction.WEST

    fun gps(){
        if (direction==Direction.WEST){
            Log.e("direction","West")
        }
    }
}

enum class  Direction{
    EAST,WEST,SOUTH,NORTH
}

并且可以通过如下的方式获取枚举变量的名称和位置

Log.e("Enum",  enumValues<Direction>().joinToString { "${it.name}:${it.ordinal}" });

打印结果如下

11-04 15:31:30.599 16041-16041/com.zhqy.javademo E/Enum: EAST:0, WEST:1, SOUTH:2, NORTH:3

其中name代表枚举常量名称,ordinal代表枚举常量位置

枚举类允许有抽象方法和实现接口
抽象继承类

/**
 * 抽象枚举类
 */
enum class AbstractEnumClass {
    MON {
        override val abstractParam: String
            get() = "星期一"

        override fun abstractFun() {
            println(abstractParam)
        }
    },
    TUES {
        override val abstractParam: String
            get() = "星期二"

        override fun abstractFun() {
            println(abstractParam)
        }
    },
    WED {
        override val abstractParam: String
            get() = "星期三"

        override fun abstractFun() {
            println(abstractParam)
        }
    };

    abstract val abstractParam: String
    abstract fun abstractFun()
}

fun main(args: Array<String>) {
    AbstractEnumClass.MON.abstractFun()//调用枚举对象实现的抽象方法 结果:星期一
    println(AbstractEnumClass.MON.abstractParam)//调用枚举对象实现的抽象属性    结果:星期一
}

实现接口

/**
 * 定义一个枚举类
 * 定义枚举类接口,接口给只读属性复制
 */
enum class EnumClass(val enumParam: String) : EnumInterface {
    MON("星期一"),
    TUES("星期二"),
    WED("星期三");//逗号隔开,分号结尾

    override fun interfaceFun() {
        println("统一实现的接口的抽象方法")
    }

    override val interfaceParam: String
        get() = "统一实现的抽象属性"
}

fun main(args: Array<String>) {
    EnumClass.MON.interfaceFun()//调用枚举对象实现的抽象方法 结果:统一实现的接口的抽象方法
    println(EnumClass.MON.interfaceParam)//调用枚举对象实现的抽象属性    结果:统一实现的抽象属性
}

interface EnumInterface {
    fun interfaceFun()
    val interfaceParam: String
}

密封类(sealed)

密封类用来表示受限的类继承结构;当一个值为有限几种的类型,而不能有任何其他类型时,在某种意义上,他们是枚举类的扩展,枚举类中每个枚举常量只存在一个实例,而密封类的一个子类可能有包含状态的多个实例。
sealed不能修饰interface,abstract class

sealed class SealedExpr{
data class Person(val num1 : Int, val num2 : Int) : SealedExpr()

object Add : SealedExpr()   // 单例模式
object Minus : SealedExpr() // 单例模式
}

// 其子类可以定在密封类外部,但是必须在同一文件中 v1.1之前只能定义在密封类内部
object NotANumber : SealedExpr() 

注意即所定义的子类都必须继承于密封类,表示一组受限的类

 var person1=sealedClass.Person("张三",10)
 var person2=sealedClass.Person("李四",11)

sealed类使用规范
(1)不允许直接实例化,本身是抽象类,允许有抽象方法
(2)不允许有非私有的构造方法
(3)sealed类及其子类必选在同一个文件中声明
(4)sealed的子类以及派生类可以不再同一个文件中

sealed类的好处:
代码保护
在使用when表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个else子句了。

sealed class SealedExpr{
    data class Person(val name : String, val age : Int) : SealedExpr()
    object Add : SealedExpr()
    companion object Minus : SealedExpr()
}

object NotANumber : SealedExpr()

fun eval(expr: SealedExpr) = when(expr){
    is SealedExpr.Add -> println("is Add")
    is SealedExpr.Minus -> println("is Minus")
    is SealedExpr.Person -> println(SealedExpr.Person("Koltin",22))
    NotANumber -> Double.NaN
}

以上就是文本的全部内容

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容