Kotlin学习日志(五)类与对象(中)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Kotlin学习日志(五)类与对象(中)

2.3 伴生对象

伴生对象这个是在Kotlin中有的,Java中没有,什么是伴生对象呢,你可以把它理解为“影子”,把类当做一个人,这个人可以有很多房子,但是人只有一个,影子也只有一个。你也可以把伴生对象替换掉静态成员的作用,但它比静态成员的功能要强大。我们之前通过性别类型来获得性别名称,那么反推呢,我们使用伴生对象来实现这一功能,新创建一个名为WildAnimalCompanion的类


package com.llw.kotlinstart.custom_class
class WildAnimalCompanion (var name: String,val sex:Int = 0) {
    var sexName:String
    init {
        sexName = if(sex == 0) "公" else "母"
    }
    fun getDesc(tag: String): String {
        return "欢迎来到$tag:这头${name}是${sexName}的"
    }
    //关键字companion表示伴随,object表示对象,WildAnimal表示伴生对象的名称
    companion object WildAnimal{
        fun judgeSex(sexName:String):Int{
            var sex:Int = when (sexName){
                "公","雄" -> 0
                "母","雌" -> 1
                else -> -1
            }
            return sex
        }
    }
}


代码应该没有什么好说的,一目了然,关键定义这个伴生对象和使用它,接下来看怎么使用

代码如下:


package com.llw.kotlinstart
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.llw.kotlinstart.custom_class.WildAnimalCompanion
import com.llw.kotlinstart.custom_class.WildAnimalFunction
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    var count: Int = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val sexArray:Array<String> = arrayOf("公","母","雄","雌")
        btn_test.setOnClickListener {
            var sexName:String = sexArray[count++%4]
            //伴生对象的WildAnimal名称可以省略掉
            //tv_result.text = "\"$sexName\"对应的类型是${WildAnimalCompanion.WildAnimal.judgeSex(sexName)}"
            tv_result.text = "\"$sexName\"对应的类型是${WildAnimalCompanion.judgeSex(sexName)}"//双引号 要加反斜杠  如同这样 \"\"
        }
    }
}

20200306165233802.png


这个是灰色的,我们省略掉也是可以的


20200306165758856.png


20200306165821685.png

有四种结果,我只放两个图

2.4 静态属性


之前我们的伴生对象可以实现静态函数,同样也能实现静态属性,只要在伴生对象内部增加几个字段定义就行了,之前是用0和1表示动物的雄雌,接下来用整型常量MALE表示雄性的0,整型常量FEMALE表示雌性的1,创建一个名为WildAnimalConstant的类,代码如下,


package com.llw.kotlinstart.custom_class
class WildAnimalConstant(var name: String, val sex: Int = MALE) {
    var sexName: String
    init {
        sexName = if (sex == MALE) "公" else "母"
    }
    fun getDesc(tag: String): String {
        return "欢迎来到$tag: 这只${name}是${sexName}的。"
    }
    companion object WildAnimal {
        //静态常量的值是不可变得,所以要使用关键字val修饰
        val MALE = 0
        val FEMALE = 1
        val UNKOWN = -1
        fun judgeSex(sexName:String):Int{
            var sex:Int = when(sexName){
                "公","雄" -> MALE
                "母","雌" -> FEMALE
                else -> UNKOWN
            }
            return sex
        }
    }
}

然后再进行调用

val sexArray:Array<String> = arrayOf("公","母","雄","雌")
        btn_test.setOnClickListener {
            var sexName:String = sexArray[count++%4]
            //伴生对象的WildAnimal名称可以省略掉
            //tv_result.text = "\"$sexName\"对应的类型是${WildAnimalCompanion.WildAnimal.judgeSex(sexName)}"
            tv_result.text = "\"$sexName\"对应的类型是${WildAnimalConstant.judgeSex(sexName)}"//双引号 要加反斜杠  如同这样 \"\"
        }


改一下类名就可以了,运行效果和之前的是一样的,只不过程序里面就可以通过WildAnimalConstant.MALE和WildAnimalConstant.FEMALE来判断公母了,不像之前通过0和1这种毫无意义的值来判断。


Kotlin的类成员分为实例成员与静态成员,实例成员包括成员属性和成员方法,其中与入参同名的成员属性可以在构造函数中直接声明,外部必须通过类的实例才能访问类的成员属性和成员方法,类的静态成员包括静态属性与静态方法,它们都在类的伴生对象中定义,外部可以通过类名直接访问该类的静态成员。


三、类的继承


我们一开始就提到了类的继承,如class MainActivity : AppCompatActivity(),这和Java是不一样的,那么Kotlin怎么定义基类并由基类派生出子类呢?


3.1 开放性修饰符


之前我们写了好多个WildAnimal类,Java和Kotlin关于类的继承还有区别,比如Java中默认每个类都能被继承,除非加了final关键字,而Kotlin刚好相反,它默认每个类都不能被继承(PS:这不是搞我心态吗!!!),这个时候要想让一个类成为基类,就要把该类开放出来,于是就用到了开放性修饰符open(PS:敲黑板,重点来了,哪个),演示代码如下:

open class Animal(var name:String,val sex:Int = 0){
    }


在Java中有几个熟悉的关键字,public、protected、private,分别表示公开、只对子类开放、私有。那么在Kotlin中也给出了4个开放性修饰符。


开放性修饰符 说明
public 对所有人开放。Kotlin的类、函数、变量不加开放性修饰符的话,默认就是public类型
internal 只对本模块内部开放,这是Kotlin新增的关键字。
protected 只对自己和子类开放
private 只对自己开放、即私有



注意到这几个修饰符与open一样都加在类和函数前面,并且都包含“开放”的意思,乍看起来还真有点迷,到底open跟这4个开放性修饰符是什么关系呢?其实很简单,open不控制某个对象的访问权限,只决定该对象能否繁衍开来,说白了,就是公告这个叼毛有没有资格繁衍下一代,只有头戴open帽子的类,才允许作为基类派生出子类来,而头戴open帽子的函数,表示它允许在子类中进行重写,如果没戴open帽子,该类就只好打光棍了,函数没戴open帽子的话,类的孩子就没有办法修改它。

至于那4个开放性修饰符,则是用来限定允许访问某对象的外部范围,通俗地说,就是哪里的帅哥可以跟这个美女搞对象,头戴public的,表示全世界的帅哥都能跟她处对象,头戴internal的,表示只有本国的帅哥可以,头戴protected的,表示自由本单位以及下属单位的可以,头戴private,表示自己本单位可以。


3.2 普通类继承


创建一个Poultry类,代码如下:


package com.llw.kotlinstart.custom_class
//Kotlin的类型默认是不能继承的(即 final类型),如果需要继承某类,该父类就应当声明open类型
open class Poultry (var name:String,val sex:Int = MALE){
    //变量、方法、类默认都是public,所以一般都把public省略掉了
    var sexName:String
    init {
        sexName = getSexName(sex)
    }
    //私有的方法既不能被外部访问,也不能被子类继承,因此open与private不能共存,否则编译器会报错
    open protected fun getSexName(sex:Int):String{
        return if(sex == MALE) "公" else "母"
    }
    fun getDesc(tag:String):String{
        return "欢迎来到$tag: 这只${name}是${sexName}的。"
    }
    companion object BirdStatic{
        val MALE = 0
        val FEMALE = 1
        val UNKOWN = -1
        fun judgeSex(sexName:String):Int {
            var sex:Int = when (sexName){
                "公","雄" -> MALE
                "母","雌" -> FEMALE
                else -> UNKOWN
            }
            return sex
        }
    }
}


然后我们再创建一个名为Pig的子类,继承Poultry,代码如下:


package com.llw.kotlinstart.custom_class
//注意父类Bird已经在构造函数声明了属性,故而子类Pig无须重复声明属性
//也就是说,子类的构造函数在输入参数前面不需要再加val和var
class Pig(name:String="猪",sex: Int= MALE) : Poultry(name, sex){
}

然后在Activity中调用:

package com.llw.kotlinstart
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.llw.kotlinstart.custom_class.*
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    var count: Int = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var sex:Int
        btn_test.setOnClickListener {
            var sexPoultry = if(count++%3==0) Poultry.MALE else Poultry.FEMALE
            var pig = Pig(sex = sexPoultry)
            tv_result.text = pig.getDesc("高老庄")
        }
    }
}

运行效果图如下:


20200312092908312.png

20200312092908312.png


然后再来定义一个小狗类 Dog的代码

package com.llw.kotlinstart.custom_class
class Dog (name:String = "哈士奇",sex:Int = MALE):Poultry(name, sex){
    override public fun getSexName(sex: Int): String {
        return if(sex == MALE) "雄" else "雌"
    }
}


然后在Activity中调用


package com.llw.kotlinstart
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.llw.kotlinstart.custom_class.*
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    var count: Int = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var sex:Int
        btn_test.setOnClickListener {
            var sexPoultry = if(count++%3==0) Poultry.MALE else Poultry.FEMALE
            var dog = Dog(sex = sexPoultry)
            tv_result.text = dog.getDesc("狗狗流浪记")
        }
    }
}

运行效果图如下:

20200312093656954.png


20200312093818612.png


3.3 抽象类


Kotlin中也存在与Java类似的抽象类,抽象类之所以存在,是因为其内部拥有被关键字abstract修饰的抽象方法。抽象方法没有具体的函数体,故而外部无法直接声明抽象类的实例,只有在子类继承时重写方法,方可使用该子类正常声明对象实例。


For Example ,鸡属于鸟类,可是公鸡和母鸡的叫声是不一样的,所以鸡这个类的叫唤方法“callOut”发出什么声音并不确定,只能先声明为抽象方法,连带着鸡类“Chicken”也变成抽象类了。


现在定义一个抽象的Chicken类,代码如下:


package com.llw.kotlinstart.custom_class
//子类的构造函数,原来的输入参数不用加var和val,新增的输入参数必须加var或者val
//因为抽象类不能直接使用,所以构造函数不必默认参数赋值
abstract class Chicken (name:String,sex:Int,var voice:String):Poultry(name, sex){
    val numberArray:Array<String> = arrayOf("一","二","三","四","五","六","七")
    //抽象方法必须在子类进行重写,所以可以省略关键字open,因为abstract方法默认就是open类型
    //open abstract fun callOut(times:Int):String
    abstract fun callOut(times:Int):String
}


然后我们从Chicken类派生出公鸡类Cock,指定攻击的叫声为“喔喔喔”,同时还要重写callOut方法,明确公鸡的叫唤行为。具体代码如下:


package com.llw.kotlinstart.custom_class
class Cock(name:String="鸡",sex:Int = Poultry.MALE,voice:String="喔喔喔"): Chicken(name, sex, voice) {
    override fun callOut(times: Int): String {
        var count = when {
            //when语句判断大于和小于时,要把完整的判断条件写到每个分支中
            times <=0 ->0
            times >=7 -> 6
            else -> times
        }
        return "$sexName$name${voice}叫了${numberArray[count]}声,这是在报晓"
    }
}


再派生出公鸡类Hen,指定攻击的叫声为“咯咯咯”,再重写callOut方法


package com.llw.kotlinstart.custom_class
class Hen(name:String="鸡",sex:Int = Poultry.FEMALE,voice:String="咯咯咯"): Chicken(name, sex, voice) {
    override fun callOut(times: Int): String {
        var count = when {
            //when语句判断大于和小于时,要把完整的判断条件写到每个分支中
            times <=0 ->0
            times >=7 -> 6
            else -> times
        }
        return "$sexName$name${voice}叫了${numberArray[count]}声,这是在下蛋"
    }
}


然后在Activity中调用不同的类


package com.llw.kotlinstart
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.llw.kotlinstart.custom_class.*
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    var count: Int = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn_test.setOnClickListener {
            //调用公鸡类
            tv_result.text = Cock().callOut(count++ % 7)
        }
    }
}
package com.llw.kotlinstart
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.llw.kotlinstart.custom_class.*
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    var count: Int = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn_test.setOnClickListener {
            //调用母鸡类
            tv_result.text = Hen().callOut(count++%7)
        }
    }
}


运行结果

20200313145923769.png

20200313145945166.png


3.4 接口


Kotlin的接口与Java一样是为了间接实现多重继承,由于直接继承多个类可能存在方法冲突等问题,因此Kotlin在编译阶段就不允许某个类同

时继承多个基类,否则会报错,于是,只能通过接口定义几个抽象方法,然后在实现该接口的具体类中重写这几个方法,从而间接实现类似C++多重继承的功能。


在Kotlin中定义接口需要注意以下几点:


(1)接口不能定义构造函数,否则编译器会报错"An interface may not have a constructor"。

(2)接口的内部方法通常要被实现它的类进行重写,所以这些方法默认为抽象类型。

(3)与Java不同的是,Kotlin允许在接口内部实现某个方法,而Java接口的所有内部方法都必须是抽象方法。


Android开发中最常见的接口是控件的点击监听器View.OnClickListener,它内部的点击动作onClick,类似的还有长按监听器、选中监听器等等,它们无一例外都定义了某种行为的事件处理过程。我们可以用一个列子来表达这些,比如鸟儿的飞翔、游泳、奔跑等,下面定义一个行为接口 Behavior。


package com.llw.kotlinstart.custom_class
//Kotlin与Java一样不允许多重继承,即不能同时继承两个及两个以上类
//否则编译器报错"Only one class may appear in a supertype list"
//所以仍然需要接口interface 来间接实现多重继承的功能
//接口不能带构造函数(那样就变成一个类了),否则编译器报错"An interface may not have a constructor"
interface Behavior {
    //接口内部的方法默认就是抽象的,所以不加abstract 也可以,当然open也可以不加
    open abstract fun fly():String
    //比如下面这个swim方法就没有加关键字abstract ,也无须在此实现方法
    fun swim():String
    //Kotlin的接口与Java的区别在于,Kotlin接口内部允许实现方法
    //此时该方法不是抽象方法,就不能加上abstract
    //不过该方法依然是open类型,接口内部的所有方法都默认是open类型
    fun run():String{
        return "大多数鸟儿跑得并不像样,只有鸵鸟、鸸鹋等少数鸟类才擅长奔跑。"
    }
    //Kotlin的接口允许声明抽象属性,实现该接口的类必须重载该属性
    //与接口内部方法一样,抽象属性前面的open和abstract 也可以省略掉
    //open abstract var skiledSporte:String
    var skiledSporte:String
}

在其他类实现这个接口时,跟类继承一样把接口名称放在冒号后面,也就是说,Java的extends和implement这两个关键字在Kotlin中都被冒号取代了。然后就想重写抽象类的抽象方法一样重写接口的抽象方法,创建一个名为Goose的类,代码如下:

package com.llw.kotlinstart.custom_class
class Goose(name: String = "鹅", sex: Int = Poultry.MALE) : Poultry(name, sex), Behavior {
    override fun fly(): String {
        return "鹅能飞一点点,但是飞不高,也飞不远"
    }
    override fun swim(): String {
        return "鹅是会游泳的"
    }
    //因为接口已经实现了run方法,所以此处可以不用实现该方法,当你也可以实现它
    override fun run(): String {
        //super用来调用父类的属性或方法,由于Kotlin的接口允许实现方法,因此super所指的对象也可以是interface
        return super.run()
    }
    //重载了来自接口的抽象属性
    override var skiledSporte: String = "游泳"
}


然后在Activity中使用

package com.llw.kotlinstart
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.llw.kotlinstart.custom_class.*
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    var count: Int = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn_test.setOnClickListener {
            tv_result.text = when(count++%3){
                0 -> Goose().fly()
                1 -> Goose().swim()
                else -> Goose().run()
            }
        }
    }
}


运行效果如下图:


调用fly方法


20200313154555580.png


调用swim方法


20200313154644639.png


调用run方法


20200313154724176.png

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2天前
|
Java Apache
学习Java中的日志系统设计与优化
学习Java中的日志系统设计与优化
|
2天前
|
Java Spring 容器
Spring5系列学习文章分享---第六篇(框架新功能系列+整合日志+ @Nullable注解 + JUnit5整合)
Spring5系列学习文章分享---第六篇(框架新功能系列+整合日志+ @Nullable注解 + JUnit5整合)
5 0
|
4天前
|
XML Java 程序员
一篇文章讲明白Log日志框架的学习五.正确使用日志的10个技巧(转载)
一篇文章讲明白Log日志框架的学习五.正确使用日志的10个技巧(转载)
|
4天前
|
XML Java 程序员
一篇文章讲明白Log日志框架的学习五.正确使用日志的10个技巧(转载)
一篇文章讲明白Log日志框架的学习五.正确使用日志的10个技巧(转载)
|
JSON Java Android开发
用kotlin打印出漂亮的android日志
用kotlin打印出漂亮的android日志
225 0
用kotlin打印出漂亮的android日志
|
29天前
|
安全 Java Android开发
使用Kotlin进行Android应用开发:高效、简洁与乐趣并存
【6月更文挑战第1天】Kotlin,JetBrains开发的静态类型语言,正日益成为Android开发首选。它与Java兼容,提供简洁、安全的语法,如空安全、扩展函数和Lambda表达式,提升开发效率和代码可读性。Kotlin在Android开发中的优势包括提高开发速度、降低学习曲线及强大的社区支持。实践中,数据类简化对象创建,扩展函数增强SDK,Lambda表达式简化回调处理,协程优化异步操作。掌握Kotlin对Android开发者极具价值。
|
2月前
|
存储 安全 Android开发
构建高效的Android应用:Kotlin与Jetpack的结合
【5月更文挑战第31天】 在移动开发的世界中,Android 平台因其开放性和广泛的用户基础而备受开发者青睐。随着技术的进步和用户需求的不断升级,开发一个高效、流畅且易于维护的 Android 应用变得愈发重要。本文将探讨如何通过结合现代编程语言 Kotlin 和 Android Jetpack 组件来提升 Android 应用的性能和可维护性。我们将深入分析 Kotlin 语言的优势,探索 Jetpack 组件的核心功能,并通过实例演示如何在实际项目中应用这些技术。
|
12天前
|
安全 Java 编译器
Android面试题之Java 泛型和Kotlin泛型
**Java泛型是JDK5引入的特性,用于编译时类型检查和安全。泛型擦除会在运行时移除类型参数,用Object或边界类型替换。这导致几个限制:不能直接创建泛型实例,不能使用instanceof,泛型数组与协变冲突,以及在静态上下文中的限制。通配符如<?>用于增强灵活性,<? extends T>只读,<? super T>只写。面试题涉及泛型原理和擦除机制。
18 3
Android面试题之Java 泛型和Kotlin泛型
|
2天前
|
安全 Android开发 Kotlin
Android面试题之Kotlin协程并发问题和互斥锁
Kotlin的协程提供轻量级并发解决方案,如`kotlinx.coroutines`库。`Mutex`用于同步,确保单个协程访问共享资源。示例展示了`withLock()`、`lock()`、`unlock()`和`tryLock()`的用法,这些方法帮助在协程中实现线程安全,防止数据竞争。
9 1
|
21天前
|
安全 Java Android开发
Kotlin与Java:Android开发的双剑合璧
【6月更文挑战第9天】Kotlin和Java在Android开发中形成互补态势。Java凭借广泛社区支持和丰富的类库资源占据主导,但其语法繁琐和空指针问题限制了发展。Kotlin,设计来解决这些问题,以其简洁、安全、高效的特性逐渐兴起。Kotlin的互操作性允许与Java无缝集成,提升开发效率,减少错误。两者结合提高了代码质量和开发者的灵活性,促进了Android开发社区的繁荣。开发者应把握这种&quot;双剑合璧&quot;,适应技术发展。
34 10