Kotlin学习日志(五)类与对象,2024年最新升职面试问题回答技巧

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

一个类可能有多个构造函数,Java可以通过覆写带不同参数的构造函数来实现,那么Kotlin已经在类名后面指明了固定数量的入参,又该如何表示拥有其他参数的构造函数呢?针对这个问题,Kotlin引入了主构造函数与二级构造函数的概念,之前的代码演示的是主构造函数,分为两部分,跟在类名后面的参数是主构造函数的入参,同时init方法是主构造函数的内部代码,至于二级构造函数,则可以在类内部直接书写完整的函数表示式,新建一个名为AnimalMain的类,代码如下:

class AnimalMain constructor(context:Context,name:String){

init {

context.toast(“这是头$name”)

}

constructor(context: Context,name: String,sex:Int) : this(context,name){

var sexName:String = if(sex ==0 ) “公” else “母”

context.toast(“这头 n a m e 是 {name}是 name{sexName}的”)

}

}

从以上代码可以看出,二级构造函数和普通函数相比有以下两个区别:

(1)二级构造函数没有函数名称,只用关键字constructor表示这是一个构造函数。

(2)二级构造函数需要调用主构造函数。“this(context,name)”这句代码在Java中要以“super(context,name)”的形式写在函数体内部,在Kotlin中则以冒号开头补充到输入参数后面,这意味着二级构造函数实际上是从主构造函数派生出来的,也可以看作二级函数的返回值是主构造函数。

由此看来,二级构造函数从属于主构造函数,如果使用二级构造函数声明该类的实例,系统就会先调用主构造函数的init代码,再调用二级构造函数的自身代码,现在若想声明AnimalMain类的实例,既可通过主构造函数,也可通过二级构造函数,代码如下:

package com.llw.kotlinstart

import androidx.appcompat.app.AppCompatActivity

import android.os.Bundle

import kotlinx.android.synthetic.main.activity_main.*

import java.lang.Exception

import java.text.SimpleDateFormat

import java.util.*

class MainActivity : AppCompatActivity() {

var animalName:String = “”

var animalSex:Int = 0

var count:Int = 0

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btn_test.setOnClickListener {

setAnimalInfo()

when(count%2){

0 -> { var animal = AnimalMain(this,animalName) }

else -> { var animal = AnimalMain(this,animalName,animalSex) }

}

count++

}

}

fun setAnimalInfo() {

animalName = “牛”

animalSex = 0

}

}

上面代码在运作过程中,通过二级构造函数声明实例有一个问题,就是toast会弹窗两次,因为主构造函数的init方法已经弹窗,然后二级构造函数自身再次弹窗,那能不能不调用主构造函数呢?为了解决该问题,Kotlin设定了主构造函数时不是必需的,也就是说类可以把几个构造函数都放在类内部定义,从而都变成二级构造函数,如此就去掉了主构造函数,为了直观,重新建名为一个AnimalSeparate的类,代码如下

package com.llw.kotlinstart

import android.content.Context

import org.jetbrains.anko.toast

class AnimalSeparate {

constructor(context: Context,name:String){

context.toast(“这是头$name”)

}

constructor(context: Context,name: String,sex:Int){

var sexName:String = if(sex ==0 ) “公” else “母”

context.toast(“这头 n a m e 是 {name}是 name{sexName}的”)

}

}

这样写就没有主构造函数了,都是二级构造函数,直接使用即可,函数之间没有从属关系,不存在重复调用。

1.3 带默认参数的构造函数


说到默认参数,不知道你有没有想起之前的带默认参数的函数呢?上面的代码中,两个构造函数之间只有一个输入参数的区别,所以完全可以把二者合二为一,变成一个带默认参数的主构造函数,新的主构造函数既能输入两个参数,又能输入三个参数,新创建一个类AnimalDefault,代码如下:

package com.llw.kotlinstart

import android.content.Context

import org.jetbrains.anko.toast

class AnimalDefault (context: Context,name:String,sex:Int = 0){

init {

var sexName:String = if(sex == 0) “公” else “母”

context.toast(“这只 n a m e 是 {name}是 name{sexName}的”)

}

}

运行效果类似,但是代码更加的简洁了。

二、类的成员

===================================================================

2.1成员属性


创建一个新的类WildAnimal,然后在构造函数中放两个参数,代码如下:

class WildAnimal(name:String,sex:Int = 0) {

}

然后我们再声明对应的属性字段,用于保存入参的数值,加入按照Java的编码思路,下面的代码应该是这样的。

class WildAnimal(name: String, sex: Int = 0) {

var name: String // 表示动物名称可以修改

val sex: Int //表示动物性别不能修改

init {

this.name = name

this.sex = sex

}

}

这上面的写法从Java的角度来看倒是没有问题,但如果时Kotlin呢,代码冗余了,

(1)属性字段跟构造函数的入参,二者名称一样,变量类型也一样。

(2)初始化函数中的属性字段赋值,为了区别同名的属性和入参,特意给属性字段添加了this。

那么Kotlin如何精简这个类的代码呢?代码如下:

class WildAnimal(var name: String,val sex: Int = 0) {

}

你没有看错,就是这样,接下来使用一下吧。

package com.llw.kotlinstart

import androidx.appcompat.app.AppCompatActivity

import android.os.Bundle

import kotlinx.android.synthetic.main.activity_main.*

import java.lang.Exception

import java.text.SimpleDateFormat

import java.util.*

class MainActivity : AppCompatActivity() {

var animalName: String = “”

var animalSex: Int = 0

var count: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btn_test.setOnClickListener {

setAnimalInfo()

var animal = when (count % 2) {

0 -> {

WildAnimal(animalName)

}

else -> {

WildAnimal(animalName, animalSex)

}

}

count++

tv_result.text = “这头 a n i m a l . n a m e 是 {animal.name}是 animal.name{if (animal.sex == 0) “公” else “母”}的”

}

}

fun setAnimalInfo() {

animalName = “牛”

animalSex = 1

}

}

再看看Java代码中怎么做的

package com.llw.kotlinstart;

public class WildAnimal {

private String name;

private String sex;

public WildAnimal(String name, String sex) {

this.name = name;

this.sex = sex;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getSex() {

return sex;

}

public void setSex(String sex) {

this.sex = sex;

}

}

很熟悉吧,因为基本上每一个实体都离不开这一步,

对比一下:

(1)冗余的同名属性声明语句。

(2)冗余的同名属性赋值语句。

(3)冗余的属性获取方法与设置方法。

Kotlin的代码真的精简了很多,鸟枪换炮,

如果某个字段并非入参的同名属性,就需要在类内部显示声明该属性字段,例如,前面WildAnimal类的性别只是一个整型的类型字段,而界面上展示的是性别的中文名称,所以应当给该类补充一个性别名称的属性字段,这样每次访问sexName字段即可获得该动物的性别名称,新建一个名为WildAnimalMember的类,代码如下:

package com.llw.kotlinstart

class WildAnimalMember (val name:String,val sex:Int = 0) {

//非空的成员属性必须在声明时赋值或者在构造函数中赋值,否则编译器会报错

var sexName:String

init {

sexName = if(sex == 0) “公” else “母”

}

}

然后再看一下怎么调用这个类:

package com.llw.kotlinstart

import androidx.appcompat.app.AppCompatActivity

import android.os.Bundle

import kotlinx.android.synthetic.main.activity_main.*

import java.lang.Exception

import java.text.SimpleDateFormat

import java.util.*

class MainActivity : AppCompatActivity() {

var animalName: String = “”

var animalSex: Int = 0

var count: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btn_test.setOnClickListener {

setAnimalInfo()

var animal = when (count % 2) {

0 -> {

WildAnimalMember(animalName)

}

else -> {

WildAnimalMember(animalName, animalSex)

}

}

count++

tv_result.text = “这头 a n i m a l . n a m e 是 {animal.name}是 animal.name{animal.sexName}的”

}

}

fun setAnimalInfo() {

animalName = “牛”

animalSex = 1

}

}

2.2 成员方法


类的成员除了成员属性还有成员方法,在类内部定义成员方法的过程和普通函数定义比较类似。下面增加一个获取动物描述信息的成员方法getDesc(),新创建一个名为WildAnimalFunction的类

package com.llw.kotlinstart

class WildAnimalFunction(var name: String, val sex: Int = 0) {

var sexName: String

init {

sexName = if (sex == 0) “公” else “母”

}

fun getDesc(tag: String): String {

return “欢迎来到 t a g :这头 tag:这头 tag:这头{name}是${sexName}的”

}

}

然后我们在MainActivity.kt中调用这个类的方法

package com.llw.kotlinstart

import androidx.appcompat.app.AppCompatActivity

import android.os.Bundle

import com.llw.kotlinstart.custom_class.WildAnimalFunction

import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

var animalName: String = “”

var animalSex: Int = 0

var count: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btn_test.setOnClickListener {

setAnimalInfo()

var animal = when(count%2){

0 -> WildAnimalFunction(

animalName

)

else -> WildAnimalFunction(

animalName,

animalSex

)

}

tv_result.text = animal.getDesc(“动物园”)

count++

}

}

fun setAnimalInfo() {

animalName = “牛”

animalSex = 1

}

}

在这里插入图片描述

在这里插入图片描述

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 “欢迎来到 t a g :这头 tag:这头 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 = arrayOf(“公”,“母”,“雄”,“雌”)

btn_test.setOnClickListener {

var sexName:String = sexArray[count++%4]

//伴生对象的WildAnimal名称可以省略掉

//tv_result.text = "“ s e x N a m e 对 ¨ 应的类型是 sexName\"对应的类型是 sexName¨应的类型是{WildAnimalCompanion.WildAnimal.judgeSex(sexName)}”

tv_result.text = "“ s e x N a m e 对 ¨ 应的类型是 sexName\"对应的类型是 sexName¨应的类型是{WildAnimalCompanion.judgeSex(sexName)}”//双引号 要加反斜杠 如同这样 “”

}

}

}

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

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

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 “欢迎来到 t a g : 这只 tag: 这只 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 = arrayOf(“公”,“母”,“雄”,“雌”)

btn_test.setOnClickListener {

var sexName:String = sexArray[count++%4]

//伴生对象的WildAnimal名称可以省略掉

//tv_result.text = "“ s e x N a m e 对 ¨ 应的类型是 sexName\"对应的类型是 sexName¨应的类型是{WildAnimalCompanion.WildAnimal.judgeSex(sexName)}”

tv_result.text = "“ s e x N a m e 对 ¨ 应的类型是 sexName\"对应的类型是 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 “欢迎来到 t a g : 这只 tag: 这只 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(“高老庄”)

}

}

}

运行效果图如下:

在这里插入图片描述

在这里插入图片描述

然后再来定义一个小狗类 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(“狗狗流浪记”)

}

}

}

运行效果图如下:

在这里插入图片描述

在这里插入图片描述

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 = 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 “ s e x N a m e sexName sexNamename v o i c e 叫了 {voice}叫了 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 “ s e x N a m e sexName sexNamename v o i c e 叫了 {voice}叫了 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)

}

}

}

运行结果

在这里插入图片描述

在这里插入图片描述

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方法

在这里插入图片描述

调用swim方法

在这里插入图片描述

调用run方法

在这里插入图片描述

3.5 接口代理


通过接口固然完成了相应行为,但是鸟类这个家族非常庞大,如果每种鸟都实现Behavior接口,工作量是非常大的,其实鸟类的行为并不多,可以分类为飞禽、水禽、走禽三个行为类

下面是飞禽的行为类代码示例:

package com.llw.kotlinstart.custom_class

class BehaviorFly : Behavior {

override fun fly(): String {

return “翱翔天空”

}

override fun swim(): String {

return “落水凤凰不如鸡”

}

override fun run(): String {

return “能飞干嘛还要走”

}

override var skiledSporte: String = “飞翔”

}

下面是水禽

package com.llw.kotlinstart.custom_class

class BehaviorSwim : Behavior {

override fun fly(): String {

return “看情况,大雁能展翅高飞,企鹅却欲飞还休”

}

override fun swim(): String {

return “怡然戏水”

}

override fun run(): String {

return “赶鸭子上树”

}

override var skiledSporte: String = “游泳”

}

下面是走禽

package com.llw.kotlinstart.custom_class

class BehaviorRun : Behavior {

override fun fly(): String {

return “飞不起来”

}

override fun swim(): String {

return “望洋兴叹”

}

override fun run(): String {

return super.run()

}

override var skiledSporte: String = “奔跑”

}

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

img

我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

.cn/20200313154724176.png#pic_center)

3.5 接口代理


通过接口固然完成了相应行为,但是鸟类这个家族非常庞大,如果每种鸟都实现Behavior接口,工作量是非常大的,其实鸟类的行为并不多,可以分类为飞禽、水禽、走禽三个行为类

下面是飞禽的行为类代码示例:

package com.llw.kotlinstart.custom_class

class BehaviorFly : Behavior {

override fun fly(): String {

return “翱翔天空”

}

override fun swim(): String {

return “落水凤凰不如鸡”

}

override fun run(): String {

return “能飞干嘛还要走”

}

override var skiledSporte: String = “飞翔”

}

下面是水禽

package com.llw.kotlinstart.custom_class

class BehaviorSwim : Behavior {

override fun fly(): String {

return “看情况,大雁能展翅高飞,企鹅却欲飞还休”

}

override fun swim(): String {

return “怡然戏水”

}

override fun run(): String {

return “赶鸭子上树”

}

override var skiledSporte: String = “游泳”

}

下面是走禽

package com.llw.kotlinstart.custom_class

class BehaviorRun : Behavior {

override fun fly(): String {

return “飞不起来”

}

override fun swim(): String {

return “望洋兴叹”

}

override fun run(): String {

return super.run()

}

override var skiledSporte: String = “奔跑”

}

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

[外链图片转存中…(img-V23xKBWV-1713561104226)]

我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-MSujrN4F-1713561104227)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值