kotlin学习--类的延申

2019-08-21  本文已影响0人  7570146919ac

一、内部类

在现实开发中,内部类出现的场景可以说非常多,例如,adapter(适配器)中的viewHolder、数据模型中也有可能出现一个或者多个对象,这里的对象就是一个内部类。让我们来了解下内部类哩。

1.1.嵌套类

所谓的嵌套类,通俗来讲就是一个类嵌套在另一个类里面
eg:

/**
 * 嵌套类
 */
 private var name = "biao"
    fun outdoorMethou(){
        //nesteMethou()//无法直接调用嵌套类的成员
        //只能通过实例嵌套类来调用
        val nestingClass = NestingClass()
        nestingClass.nesteMethou()
    }

    class NestingClass{
        fun nesteMethou(){
            //println("name:$name")//无法使用外部类的成员
            println("to do something magic")
        }
    }

fun main(args:Array<String>){
    val nestingClass = OutdoorClass.NestingClass()
    nestingClass.dowhat()
}

reslut:

to do something magic

说明:

1.2.内部类

内部类相当于Java中没有用static修饰的内部类,使用关键字:inner
eg:

class OutClass{

    //外部类的私有属性可以被内部类使用
    private var name = "xie"
    //外部类的私有函数可以被内部类使用
    private fun outFun(){
        println("i am outFun")
    }

    //通过此方法实例化内部类,再调用内部类方法
    fun getInnerClassTodo(){
        //innerFun()//这种外部类直接调用内部类成员的方式是编译不通过的,因为此时根本不存在内部类的对象
        val innerClass = InnerClass()
        //外部类想调用内部类,需实例化内部类
        innerClass.innerFun()
    }

    inner class InnerClass{
        var age = 20

        fun innerFun(){
            println("name:$name+age:$age")
            outFun()
        }
    }
}

fun main(args:Array<String>){
    val innerClass = OutClass().InnerClass()
    innerClass.innerFun()
}

reslut:

name:xie+age:20
i am outFun

说明:

1.3.匿名内部类

在实际开发中,匿名内部类并不陌生,随处可见,像view的点击事件:OnClickListener,多线程的实现。
其实匿名内部类就是一个没有名字的内部类,通常用来简化代码,使用匿名内部类的前提条件是:必须继承一个父类或者实现一个接口
eg:

/**
 * 定义一个接口
 */
interface OnClickListener{
    fun onItemClick(str : String)
}

/**
 * 匿名内部类
 */
class AnonymousClass{
    lateinit private var listener : OnClickListener

    fun setOnClickListener(listener: OnClickListener){
        this.listener = listener
    }

    fun testListener(){
        listener.onItemClick("我是匿名内部类的测试方法")
    }
}

fun main(args:Array<String>){
    val anonymousClass = AnonymousClass()
    anonymousClass.setOnClickListener(object :OnClickListener{
        override fun onItemClick(str: String) {
            println("to do something")
        }
    })
}

1.4.局部类

局部类:跟Java一样,就是在方法中的类
eg:

class LocalClass{

    var numOut = 1

    fun partMouth(){

        var name = "partMouth"

        class PartClass{
            var numPart : Int = 2

            fun test(){
                name = "PartClass"
                numOut = 3
                numPart = 4
                println("我是局部类中的方法")
            }
        }

        val partClass = PartClass()
        println("name = $name \t numPart = " + partClass.numPart + "\t numOut = $numOut")
        partClass.test()
        println("name = $name \t numPart = " + partClass.numPart + "\t numOut = $numOut")
    }
}

fun main(args:Array<String>){
    val localClass = LocalClass()
    localClass.partMouth()
}

result:

name = partMouth     numPart = 2     numOut = 1
我是局部类中的方法
name = PartClass     numPart = 4     numOut = 3

说明:

二、抽象类

在我们日常开发中,一般会需要写一个基类,封装一些方法以及处理一些共有的逻辑,只是不同的类会因其不同的功能实现不同的代码。就这样的一个基类,一般都是一个抽象类

2.1.抽象类的定义

抽象类:可以理解为一个模板,所有的子类根据这个模板填充自己的代码

2.1.1 关键字

跟Java一样,kotlin抽象类的关键字也是abstract
在这里我们需要注意的是抽象分为抽象类、抽象函数和抽象属性,而一个抽象类和一个普通类的区别就在于抽象类的除了可以有其自己的属性、构造函数和函数等组成部分,还包含了抽象函数和抽象属性。
eg:

abstract class AbstractClass {
    //自身的属性
    var tag = this.javaClass.simpleName

    //自身的方法
    fun simpleFun():Unit{//Unit相当于java中的void,表示没有返回值,可以省略不写
        TODO("do something")
    }

    //抽象属性和方法
    abstract var string:String
    abstract fun init()
}

class ChildrenClass : AbstractClass() {
    //不是抽象类的子类必须实现父类的方法和属性
    override var string: String
        get() = tag
        set(value) {}

    override fun init() {
        println("Implementing parent class methods,Obtain tag:$string")
    }
}

abstract class AbsChildrenClass : AbstractClass(){
    //抽象子类可不用实现父类的方法和属性
    var name = this.javaClass.simpleName
    abstract fun check()
}

class absExtend : AbsChildrenClass(){
    var className = this.javaClass.simpleName
    override var string: String
        get() = name
        set(value) {}

    override fun init() {
        println("name:$name")
    }

    override fun check() {
        if (className == string) println("do something")
        else println("Nothing can be done")
    }
}

fun main(args : Array<String>){
    //抽象类不能直接实例化
    //val abstractClass = AbstractClass()//Cannot create an instance of an abstract class
    val childrenClass = ChildrenClass()
    var abstractClass:AbstractClass = ChildrenClass()//若要实现抽象类的实例化,需要依靠子类采用向上转型的方式处理
    val absExtend = absExtend()

    childrenClass.init()
    absExtend.init()
    absExtend.check()
}

result:

Implementing parent class methods,Obtain tag:ChildrenClass
name:absExtend
do something

小结:

规则:

三、接口类

3.1 声明以及用法

3.1.1 声明

关键字:interface
定义格式:interface 接口名{
}

3.1.2 用法

关键字:“:”,表示实现该接口,Java不一样的是,Java使用implement关键字
综合之前我之前写过的文章,内容使用到:的次数表示,这里讲一下:有哪些代表含义:
a、用于属性的定义
b、用于表示实现接口
c、用户继承
d、用于方法返回类型定义

eg:

interface InterfaceClass{
    fun funOne()
}

class Demo:InterfaceClass{
    override fun funOne() {
        println("funOne are using")
    }
}

fun main(args: Array<String>) {
    val demo = Demo()
    demo.funOne()
}

result:

funOne are using

3.2.接口中的方法使用

eg:

interface InterfaceClass {

    fun funOne()

    fun funTwo(num: Int)

    fun funThree(num: Int): Int

    /**
     * 定义了一个有参无返回值的方法
     * 因为有结构体故可以不用重写
     */
    fun funFour(): String {
        return "I am funFour"
    }
}

class Demo : InterfaceClass {
    override fun funOne() {
        println("funOne are using")
    }

    override fun funTwo(num: Int) {
        println("funTwo are using and parem is num:$num")
    }

    override fun funThree(num: Int): Int {
        println("funThree are using and parem is num:$num")
        return num + 1
    }

    /**
     * 接口中的funFour()方法默认返回”funFour“字符串.
     * 可以用super.funFour()返回默认值
     * 也可以不用super关键字,自己返回一个字符串
     */
    override fun funFour(): String {
        println("user I am funFour")
        return super.funFour()
    }
}

fun main(args: Array<String>) {
    val demo = Demo()
    demo.funOne()
    demo.funTwo(8)
    println(demo.funThree(9))
    demo.funFour()
}

result:

funOne are using
funTwo are using and parem is num:8
funThree are using and parem is num:9
10
user I am funFour
I am funFour

说明:

3.3.接口中的属性使用

3.3.1 作为抽象的

eg:

interface InterfaceFeild {
    var num:Int
    var name:String
}

class Demo1(override var num: Int, override var name: String) :InterfaceFeild{
    fun test(){
        println("num:$num\t+name:$name")
    }
}

fun main(args: Array<String>) {
    val demo1 = Demo1(21, "biao")
    demo1.test()
}

result:

num:21 name:biao

说明:

3.3.2 作为访问器

手动方式去重写,并提供get方法

eg:

interface InterfaceFeild {
    var num:Int
    var name:String
    val age:Int
        get() = 2

    var number:Int
}

class Demo1(override var num: Int, override var name: String) :InterfaceFeild{

    fun test(){
        println("age:$age\tnumber:$number")
    }

    override val age: Int
        get() = super.age

    override var number: Int = 25
}

fun main(args: Array<String>) {
    val demo1 = Demo1(21, "biao")
    demo1.test()
    demo1.number = 3
    demo1.test()
}

result:

num:21  name:biao
age:2   number:25
num:21  name:biao
age:2   number:3
3.3.3 接口的冲突问题解决

该问题是指当我们在父类中声明了许多类型,有可能出现一个方法的多种实现

eg:

interface InfOne{
    fun funTest(){
        println("I am InfOne")
    }
}

interface InfTwo{
    fun funTest(){
        println("I am InfTwo")
    }
}

class Demo2 : InfOne,InfTwo{
    override fun funTest() {
        super<InfOne>.funTest()
        super<InfTwo>.funTest()
    }

}

fun main(args: Array<String>) {
    val demo2 = Demo2()
    demo2.funTest()
}

说明:Demo4实现了InfOne和InfTwo两个接口,而两个接口中都存在两个相同方法名的方法。因此编译器不知道应该选哪个,故而我们用super<接口名>.方法名来区分。

四、继承类

4.1 超类 Any

在Kotlin中,说有的类都是继承与Any类,这是这个没有父类型的类。即当我们定义各类时,它默认是继承与Any这个超类的

来看看Any的源码

package kotlin

/**
 * The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
 * 看这个源码注释:意思是任何一个Kotlin的类都继承与这个[Any]类
 */
public open class Any {
    
    // 比较: 在平时的使用中经常用到的equals()函数的源码就在这里额
    public open operator fun equals(other: Any?): Boolean
    
    // hashCode()方法:其作用是返回该对象的哈希值
    public open fun hashCode(): Int
    
    // toString()方法
    public open fun toString(): String
}

从源码可以我们看出,它直接属于kotlin这个包下。并且只定义了上面所示的三个方法。或许你具有Java的编程经验。在我们熟知的Java中,所有的类默认都是继承与Object类型的。而Object这个类除了比Any多了几个方法与属性外,没有太大的区别。
此外,从上面的源码中我们可以看到类和函数都有open关键字,那么这个修饰符的作用是什么呢?
来,让我们来分析一下,超类(Any)是所有类的父类,而分类中所有元素都有open修饰符,如果我们跟Any的语法去写一个类,是不是就是一个继承类了,说明,open修饰符是我们定义继承类的修饰符

4.2 定义

4.2.1 基础使用
open class 类名{
    open var/val 属性名 = 属性值
    open fun 函数名()
}

eg:

open class InheritClass{
    open var num = 3
    open fun funOne() = "funOne"
    open fun funTwo() = 25
}

class DemoTest : InheritClass(){
    // 这里值得注意的是:Kotlin使用继承是使用`:`符号,而Java是使用extends关键字
}

fun main(args:Array<String>){
    val demoTest = DemoTest()
    println(demoTest.num)
    println(demoTest.funOne())
    println(demoTest.funTwo())
}

result:

3
funOne
25

说明:
从上面的代码可以看出,DemoTest类只是继承了Demo类,并没有实现任何的代码结构。一样可以使用Demo类中的属性与函数。这就是继承的好处。

4.2.2 构造函数

在上次的分享中有提到过,在kotlin中,可以有一个主构造函数,或者多个辅助函数。或者没有构造函数的情况。

在这里,来说一下实现类无主构造函数,和存在主构造函数的情况。

当实现类无主构造函数时,则每个辅助构造函数必须使用super关键字初始化基类型,或者委托给另一个构造函数。 请注意,在这种情况下,不同的辅助构造函数可以调用基类型的不同构造函数

这里用view的自定义来说明一下,eg:

class MyView : View(){

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
        : super(context, attrs, defStyleAttr)
}

可以看出,当实现类无主构造函数时,分别使用了super()去实现了基类的三个构造函数。

当存在主构造函数时,主构造函数一般实现基类型中参数最多的构造函数,参数少的构造函数则用this关键字引用即可了。这点在Kotlin——中级篇(一):类(class)详解这篇文章是讲解到的。

这里同样用view自定义的例子,eg:

class MyView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int)
    : View(context, attrs, defStyleAttr) {

    constructor(context: Context?) : this(context,null,0)
    
    constructor(context: Context?,attrs: AttributeSet?) : this(context,attrs,0)
}
4.2.3 函数的重载和重写

kotlin的重载和重写的理解和Java一样,重载:是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同;重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写!

4.2.3.1 重写函数中的两点特殊用法

这里介绍两点kotlin相较于Java特殊的地方

eg:

open class InheritClass{
    fun test(){}
}

class DemoTest : InheritClass(){
    // 这里声明一个和基类型无open修饰符修饰的函数,且函数名一致的函数
    // fun test(){}   编辑器直接报红,根本无法运行程序
    // override fun test(){}   同样报红
}
4.2.3.1 方法重载

方法重载也是体现了面向对象中的多态的特性
方法重载在继承类同样有效
eg:

open class InheritClass{
    open fun funThree() = "funThree"
}

class DemoTest : InheritClass(){
    fun funThree(name:String):String{
        return name
    }
    override fun funThree(): String {
        return super.funThree()
    }
}

fun main(args:Array<String>){
    val demoTest = DemoTest()
    println(demoTest.funThree())
    demoTest.funThree("I am funThree,Do you known polymorphic?")
}

result:

funThree
I am funThree,Do you known polymorphic?
4.2.4 重写属性

eg:

open class InheritClass{
    open var num = 3
}

class DemoTest : InheritClass(){
    override var num: Int = 10
}

fun main(args:Array<String>){
    val demoTest = DemoTest()
    println(demoTest.num)
}

result:

10
4.2.4.1 重写属性中,val与var的区别

有一点值得我们注意的是当基类中属性的变量修饰符为val的使用,其实现类可以用重写属性可以用var去修饰。反之则不能。
eg:

open class InheritClass {
    open val valStrOne = "I am val string one"
    open val valStrTwo = "I am val string two"
    open val valStrThree = "I am val string three"
    open val valStrFour = "I am val string four"
}

class DemoTest : InheritClass() {
    override val valStrOne: String
        get() = super.valStrOne

    override var valStrTwo: String = ""
        get() = super.valStrTwo

    override val valStrThree: String = ""

    override var valStrFour: String = "abc"
        set(value){field = value}
}

fun main(args: Array<String>) {
    val demoTest = DemoTest()
    println(demoTest.valStrOne)
    println(demoTest.valStrTwo)
    println(demoTest.valStrThree)
    println(demoTest.valStrFour)
    demoTest.valStrFour = "I am Icon man"
    println(demoTest.valStrFour)
}

result:

I am val string one
I am val string two
I am val string three
I am val string four
I am Icon man
4.2.4.2、Getter()函数慎用super关键字

在这里值得注意的是,在实际的项目中在重写属性的时候不用get() = super.xxx,因为这样的话,不管你是否重新为该属性赋了新值,还是支持setter(),在使用的时候都调用的是基类中的属性值。

eg:

open class InheritClass {
    open val valStrFive = "I am val string five"
}

class DemoTest : InheritClass() {
    override var valStrFive: String = "cba"
        get() = super.valStrFive
        set(value){field = value}
}

fun main(args: Array<String>) {
    val demoTest = DemoTest()
    println(demoTest.valStrFive)
    demoTest.valStrFive
    println(demoTest.valStrFive)
}

result:

I am val string five
I am val string five
上一篇 下一篇

猜你喜欢

热点阅读