Kotlin学习笔记(三)—面向对象(3)

13.数据类

在Koltin中,在类前添加关键字data可以将类转换为数据类。Koltin的数据类可以默认帮我们实现一些例如toString,equals等方法,
在使用data class的对象时,可以使用data class 对象的componentN()方法区访问对象的第N个属性。
也可以使用(arg1,arg2)= data的方式去访问data对象的属性 。

但相反,Kotlin数据类有一些与生俱来的缺陷

  • 不可被继承
  • 不能声明无参数构造函数

使用noarg插件和allopen插件可以帮助解决这些问题。使用方法:

1.在project的build.gradle的dependence里添加插件

classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
  classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"

2.在app的build.gradle文件中(最外层即可),引用插件:

apply plugin:"kotlin-noarg"
apply plugin:"kotlin-allopen"

3.声明注解类 例如PoKo
4.将注解类的引用地址配置给allopen与noarg

noArg {
    annotation("com.fatfat.ktjetpack.data.PoKo")
}
allOpen {
    annotation("com.fatfat.ktjetpack.data.PoKo")
}

5.在需要操作的数据类上打定义好的注解

@PoKo
data class User(var name:String) {
}

要注意,虽然这种方法能帮助数据类型生成无参构造函数以及去掉final关键字,但仅限于编译期,所以我们在编码的过程中只能通过反射的方式进行访问数据类的无参构造函数。这两个插件底层的机制是字节码修改。

14.内部类

14.1 静态内部类与非静态内部类

顾名思义内部类就是定义在类内部的类。内部类可以分为两种,一种是静态内部类,另外一种是非静态内部类。
这两种类的区别就是静态内部类不持有外部类的应用。
举个🌰:

public class Outer {
    public int a;
    class Inner{
        public void sayhello(){
            System.out.println(a);
            //此处相当于Outer.this.a
        }
    }
    static class SInner{
        public void sayhello(){
            System.out.println(a);//此处会出现语法错误提示!!!!❌
        }
    }
   public static void main(String[] args) {
        Outer outer = new Outer();
        Inner inner = outer.new Inner();
        SInner sInner = new SInner();
    }
}

我们可以看到,静态内部类是不能访问类中的属性的。原因是静态内部类没有持有外部类的引用。究其根本原因,是因为静态类是比非静态类要先编译的,也就是说在编译静态类的时候,它所在的外部类对它来说是不可见的,所以没法持有外部类的引用,也自然没法访问外部类的属性。

当我们创建对象时,静态内部类是可以直接使用创建外部类对象的方式去创建对象的。而内部类只能通过外部类的对象去创建自己的对象。

看个kotlin的🌰:

class Outer {
    val a = 1

    class SInner{
        fun hello(){
            println(a)//此处会出现编译错误!!!!❌
        }
    }
    inner class Inner{
        fun hello(){
            println(a)
            //此处相当于this@Outer.a
        }
    }
}

fun main() {
    var outer = Outer()
    var inner = Outer.SInner()
    var sInner = outer.Inner()
}

Kotlin与Java的去比恩就在于 Java内部类如果不加static关键字修饰,默认是非静态内部类。而Kotlin内部类如果不加inner关键字修饰,默认是静态内部类。

14.2 匿名内部类

如果我们只关注于类内部方法的实现,而并不在乎这个对象的属性的话,就可以使用匿名内部类。举个Java🌰:

public class View {
    public OnClickListener onClickListener;

    public OnClickListener getOnClickListener() {
        return onClickListener;
    }

    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    public static void main(String[] args) {
        View view = new View();
        //这里就是匿名内部类的使用
        view.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick() {
                
            }
        });
    }
}
interface OnClickListener{
    void onClick();
}

在看个kotlin的例子:

class ViewK {

    var onClickListener: OnClickListener? = null

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val view = ViewK()
            view.onClickListener = object :ViewG(), OnClickListener {
                override fun onClick() {}
            }
        }
    }
}
open class ViewG{}
interface OnClickListener {
    fun onClick()
}

我们可以看出来,Java的匿名内部类是不能继承别的类或者实现别的借口的。而Kotlin的匿名内部类可以继承别的类。

匿名内部类虽然被叫做匿名内部类,但其实在运行的过程中,它会被分配一个ID。这个ID一般为匿名内部类的引用+$N。

15 枚举类

一个例子:

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

    fun getTag()= "$id,$name"

    override fun toString(): String {
        return "$name ,$ordinal  "
    }

}

fun main() {
    println(LogLevel.DEBUG.getTag())
    println(LogLevel.DEBUG.ordinal)//获取定义顺序
    println(LogLevel.values().map(::print))//获取枚举中全部元素
    println(LogLevel.valueOf("ERROR"))//获取某一个元素
}

总的来说,与Java中枚举的使用无较大差别。