kotlin入门

Posted 临风而眠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kotlin入门相关的知识,希望对你有一定的参考价值。

Kotlin入门(2)

类与继承

一.引入

下面围绕此情景展开:

​ 基类为住处Dwelling,子类为方形小木屋 (SquareCabin)、圆形小屋 (RoundHut) 和圆形塔楼 (RoundTower)(即多楼层的 RoundHut

也就是将要实现以下的类:

  • Dwelling:一个表示非特定住房的基类,可存储所有住房通用的信息。
  • SquareCabin:用木材建成的方形小木屋,建筑平面为方形。
  • RoundHut:用稻草搭成的圆形小屋,建筑平面为圆形,它是 RoundTower 的父级。
  • RoundTower:用石头建成的多楼层圆形塔楼,建筑平面为圆形。

属性有:

方法有:

二.创建父类

先看代码

abstract class Dwelling(private var residents: Int) {
	//实际人数,可能会变,用var
   abstract val buildingMaterial: String	//建筑材质,一般不会变,用val
   abstract val capacity: Int	//最大人容量,一般不会变,用val

   fun hasRoom(): Boolean {
       return residents < capacity
   }
}

我们从C++的写法跳到这里肯定会对abstract是啥有疑问

1.why abstract

abstract,抽象的,顾名思义,抽象的没法和具体的东西一样,所以abstract类就相当于给一类东西抽象出一个概念

比如房屋,房屋有哪些属性呢,体积:高矮胖瘦;结构:卷我屋上三重茅或者固若金汤混凝土;价格:曹县一张床,浦东一套房…

房屋这个概念所包含的东西太多了,而从中抽出随便一种建筑,你的属性都可以从房屋这个概念里面抽取变成具体的子类

buildingMaterial和capacity属性作为抽象类的属性,也无法具体给值,所以也加上abstract 关键字

2.private

private标记的属性只能在此类中使用,(没有用private的属性和方法默认为public,这个和C++差不多)

放在情境中看,住的人数应该是隐私信息,也合理

3.构造函数

abstract class Dwelling(private var residents: Int)

和C++差别较大,构造函数就写在括号内

注:

抽象类无法实例化一个对象

比如下面这个代码就会报错

val a = Dwelling()

三.创建子类

还是先看代码:

fun main() {
    val squareCabin = SquareCabin(6)

    with(squareCabin) {
        println("\\nSquare Cabin\\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }
    val roundHut = RoundHut(3)
    with(roundHut) {
    println("\\nRound Hut\\n=========")
    println("Material: ${buildingMaterial}")
    println("Capacity: ${capacity}")
    println("Has room? ${hasRoom()}")
}
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}
class RoundHut(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Straw"
    override val capacity = 4
}

运行结果如下:

image-20210616005700895

1.继承关系

需要在子类后面加冒号

class SquareCabin : Dwelling()

前面说到了Dwelling的构造函数,构造函数是自动调用的,而Dwelling的构造函数需要传参,所以括号里要传个Int型的数字

class SquareCabin : Dwelling(11)

但这样不方便后期修改程序,可以将父类的residents作为参数,这也同时构成了SquareCabin的构造函数

class SquareCabin(residents: Int) : Dwelling(residents)

传入参数给SquareCabin的时候,相当于赋值给了residents,于是先调用子类的构造函数,再调用父类的构造函数

2.override

前面是abstract,abstract关键字是不能实例化的,所以就要使其能够作为 可以实例化的子类 的属性

SquareCabinDwelling 的子类,因此它必须为 buildingMaterial 提供值。使用 override 关键字来表明此属性是在父类中定义的,并且在此类中将被替换掉

3.with

简化作用,如果没有with,前面相应的代码要改成

fun main() {
    val squareCabin = SquareCabin(6)

    println("\\nSquare Cabin\\n============")
    println("Capacity: ${squareCabin.capacity}")
    println("Material: ${squareCabin.buildingMaterial}")
    println("Has room? ${squareCabin.hasRoom()}")
}

所以with语句的意思就是对with后面括号里面的对象执行{}内的操作,不需要输入”对象.属性“或者”对象.方法“

hasRoom() 函数不是在 SquareCabin 类中定义的,而是在 Dwelling 类中定义的。由于 SquareCabinDwelling 类的子类,hasRoom() 函数没有添加abstract关键字,可以直接继承。

现在,可以在 SquareCabin 的所有实例上调用 hasRoom() 函数,如上面代码段所示

4.继续继承

引入中讲到

RoundHutRoundTower 的父级。

可是我们用下面这段代码会报错:

class RoundTower(residents: Int) : RoundHut(residents) {
    override val buildingMaterial = "Stone"
    override val capacity = 4
}

报错如下:

This type is final, so it cannot be inherited from

在 Kotlin 中,类是最终层级,无法被子类化。只能从 abstract 类或标记有 open 关键字的类继承。因此,使用 open 关键字标记 RoundHut 类,使其能够被继承

完整代码如下(给RoundTower添加了楼层属性,给了默认值2)

fun main() {

    val squareCabin = SquareCabin(6)
    val roundHut = RoundHut(3)
    val roundTower = RoundTower(4)

    with(squareCabin) {
        println("\\nSquare Cabin\\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }

    with(roundHut) {
        println("\\nRound Hut\\n=========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }

    with(roundTower) {
        println("\\nRound Tower\\n==========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

open class RoundHut(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Straw"
    override val capacity = 4
}

class RoundTower(
    residents: Int,
    val floors: Int = 2) : RoundHut(residents) {

    override val buildingMaterial = "Stone"
    override val capacity = 4 * floors
}

不给floors默认值也可以这样,也就是多传一个参数

val roundTower = RoundTower(4,1)

class RoundTower(
    residents: Int,
    val floors: Int) : RoundHut(residents) {

    override val buildingMaterial = "Stone"
    override val capacity = 4 * floors
}

增设情景

​ 下面还将增计算建筑面积允许新客获得房间为圆形住房准备合适的地毯,先看完整代码

/**
* 各种房屋类
* 主要包含:
* 方法和属性的继承
* abstract class, overriding, and private 与 public 变量.
*/

import kotlin.math.PI
import kotlin.math.sqrt

fun main() {
   val squareCabin = SquareCabin(6, 50.0)
   val roundHut = RoundHut(3, 10.0)
   val roundTower = RoundTower(4, 15.5)

   with(squareCabin) {
       println("\\nSquare Cabin\\n============")
       println("Capacity: ${capacity}")
       println("Material: ${buildingMaterial}")
       println("Floor area: ${floorArea()}")
   }

   with(roundHut) {
       println("\\nRound Hut\\n=========")
       println("Material: ${buildingMaterial}")
       println("Capacity: ${capacity}")
       println("Floor area: ${floorArea()}")
       println("Has room? ${hasRoom()}")
       getRoom()
       println("Has room? ${hasRoom()}")
       getRoom()
       println("Carpet size: ${calculateMaxCarpetSize()}")
   }

   with(roundTower) {
       println("\\nRound Tower\\n==========")
       println("Material: ${buildingMaterial}")
       println("Capacity: ${capacity}")
       println("Floor area: ${floorArea()}")
       println("Carpet size: ${calculateMaxCarpetSize()}")
   }
}

/**
* 定义了所有住处都有的属性
* 每种房屋都有楼层面积,
* 但楼层面积的计算方法不同(受形状影响),要由子类具体复写实现.
* 判断客房是否满了还有允许新客获得房间在Dwelling类里面就实现了,这样它就能够用于所有子类及其子级 
* 因为每个子类的这两个方法都是一样的.
*
* @param residents Current number of residents
*/
abstract class Dwelling(private var residents: Int) {
   abstract val buildingMaterial: String
   abstract val capacity: Int

   /**
    * 计算楼层面积.
    * 由各子类具体复写实现.
    *
    * @return floor area
    */
   abstract fun floorArea(): Double

   /**
    * 判断是否能容纳新客.
    *
    * @return true if room available, false otherwise
    */
   fun hasRoom(): Boolean {
       return residents < capacity
   }

   /**
    * 比较容量和实际人数
    * 如果容量
    * 通过增加数字体现人数增加
    * 输出结果
    */
   fun getRoom() {
       if (capacity > residents) {
           residents++
           println("You got a room!")
       } else {
           println("Sorry, at capacity and no rooms left.")
       }
   }

   }

/**
* 方形小屋
*
*  @param residents Current number of residents
*  @param length Length
*/
class SquareCabin(residents: Int, val length: Double) : Dwelling(residents) {
   override val buildingMaterial = "Wood"
   override val capacity = 6

   /**
    * 计算方形小屋的楼层面积
    *
    * @return floor area
    */
   override fun floorArea(): Double {
       return length * length
   }

}

/**
* 圆形小屋
*
* @param residents Current number of residents
* @param radius Radius
*/
open class RoundHut(
       val residents: Int, val radius: Double) : Dwelling(residents) {
//radius 半径
   override val buildingMaterial = "Straw"
   override val capacity = 4

   /**
    * 计算圆形小屋的楼层面积
    *
    * @return floor area
    */
   override fun floorArea(): Double {
       return PI * radius * radius
   }

   /**
    *  计算最大的适用于圆形小屋的方形地毯的面积
    *
    * @return length of carpet
    */
   fun calculateMaxCarpetSize(): Double {
       val diameter = 2 * radius //diameter直径
       return sqrt(diameter * diameter / 2)
   }

}

/**
* 有多层楼的圆形塔楼
*
* @param residents Current number of residents
* @param radius Radius
* @param floors Number of stories
*/
class RoundTower(
       residents: Int,
       radius: Double,
       val floors: Int = 2) : RoundHut(residents, radius) {

   override val buildingMaterial = "Stone"

   //容量由楼层决定.
   override val capacity = floors * 4

   /**
    * 计算圆形塔楼面积
    *
    * @return floor area
    */
   override fun floorArea(): Double {
       return super.floorArea() * floors
   }
}

对代码的注释进行补充:

  • Dwelling 具有构造函数参数 residents,因此其所有子类都具有该参数。由于这是 Dwelling 构造函数中的第一个参数,所以最好也将其作为所有子类构造函数中的第一个参数,并在所有类定义中以相同的顺序排列参数。因此,各个子类插入新参数的时候最好都在 residents 后面插入
  • 导入语句要放在文件顶部,main函数前面
  • RoundTower 类定时要具有与其父级 RoundHut 相同的 radius 参数(从父类扩展,您必须传入父类所需的参数)
  • 使用如下代码
override fun floorArea(): Double {
       return super.floorArea() * floors
   }

是使用了 super 关键字,调用父级中定义的函数,好处是可以避免类似的代码

override fun floorArea(): Double {
    return PI * radius * radius * floors
}

四.总结

(官方文档上的总结)

  • 创建一个类层次结构,这是一种包含类的树形结构,其中子级会继承父类的函数。子类继承的有属性和函数。
  • 创建一个 abstract 类,在这种类中,部分函数会留给其子类来实现。因此,abstract 类无法被实例化。
  • 创建 abstract 类的子类。
  • 使用 override 关键字,在子类中替换属性和函数。
  • 使用 super 关键字,引用父类中的函数和属性。
  • 将一个类标记为 open,使其能够被子类化。
  • 将一个属性标记为 private,使其只能在相应类中使用。
  • 使用 with 构造函数,在同一对象实例上进行多次调用。
  • kotlin.math 库导入函数

​ 突然想到红警里面各种建筑了(那种可以把美国大兵塞进去的),有的可以放很多美国大兵,有的抗打,有茅草屋,有碉堡…把那个情景引入进来挺好的哈哈哈😺

以上是关于kotlin入门的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Kotlin 片段内的按钮之间切换片段?

kotlin-从一个片段更改多个片段的小数位

如何更改片段 Kotlin

如何在kotlin的片段内显示网格视图?

如何在kotlin中从一个片段转到另一个片段?

在片段中单击按钮播放声音(Kotlin)[重复]