1.类的构造
1.1类的简单定义
//java
public class MainActivity extends AppCompatActivity {
...
}
//Kotlin
class MainActivity : AppCompatActivity() {
...
}
Kotlin对类的写法有以下特点:
<1>省略了关键字public,缘于它默认就是开放的;
<2>用冒号“:”代替extends,表示继承关系;
<3>进行继承时,父类后面多了括号()
class Animal {
//类的初始化函数init
init {
//Kotlin使用println代替Java的System.out.println
println("Animal:这是个动物类")
}
}
var animal = Animal()//类的实例创建,忽略new关键字
1.2 类的构造函数
//如果主构函数没有带@符号的注解说明,constructor就可以省略
//class AnimalMain (context:Context, name:String) {
class AnimalMain constructor(context:Context, name:String) {
init {
context.toast("这是只$name")
}
}
//含二级构造函数
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("这只${name}是${sexName}的")
}
}
<1>二级构造函数没有函数名称,只用关键字constructor表示这是一个构造函数;
<2>二级构造函数需要调用主构函数。
//构造函数调用
btn_class_main.setOnClickListener {
setAnimalInfo()
when (count%2) {
0 -> { var animal = AnimalMain(this, animalName) }
else -> { var animal = AnimalMain(this, animalName, animalSex) }
}
}
上述二级构造函数存在一个问题,会调用到主构函数的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("这只${name}是${sexName}的")
}
}
1.3 带默认参数的构造函数
class AnimalDefault (context: Context, name:String, sex:Int = 0) {
init {
var sexName:String = if(sex==0) "公" else "母"
context.toast("这只${name}是${sexName}的")
}
}
var animal = AnimalDefault(this, animalName)//调用,默认sex为“公”
注:上述类如果想要Java类识别默认参数,需要加入“@JvmOverloads”注解:
//因为加入了注解标志@,需要补上constructior关键字
class AnimalDefault @JvmOverloads constructor(context: Context, name:St
ring, sex:Int = 0) {
init {
var sexName:String = if(sex==0) "公" else "母"
context.toast("这只${name}是${sexName}的")
}
}
AnimalDefault animal = new AnimalDefault(this, animalName);//java 代码调用
2.类的成员
2.1成员属性
class WildAnimal (var name:String, val sex) {
}
animal.name //相当于Java的get方法
成员变量需要var或val声明,调用只需对象.成员变量即可。
2.2 成员方法
class WildAnimalFunction (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}的"
}
}
animal.getDesc("动物园")//调用
2.3 伴生对象
class WildAnimalCompanion (var name:String, val sex:Int = 0) {
...
//在类加载时就运行伴生对象的代码块,其作用相当于Java里面的static{...}代码块
//在关键字companion表示伴随,object表示对象
companion object WildAnimal{
fun judgeSex(sexName:String):Int {
var sex:Int = when (sexName) {
"公","雄" -> 0
"母","雌" -> 1
else -> -1
}
return sex
}
}
}
WildAnimalCompanion.WildAnimal.judgeSex(sexName)//调用
WildAnimalCompanion.judgeSex(sexName)//WildAnimal可省略
2.4 静态属性
companion object WildAnimal{
//静态常量的值是不可变的,所以要使用关键字val修饰
val MALE = 0
val FEMALE = 1
val UNKNOWN = -1
fun judgeSex(sexName:String):Int {
var sex:Int = when (sexName) {
"公","雄" -> MALE
"母","雌" -> FEMALE
else -> UNKNOWN
}
return sex
}
}
WildAnimalCompanion.MALE//调用
3 类的继承
3.1开放性修饰词
开放性修饰词 | 说明 |
public | 对所有人开放。Kotlin的类、函数、变量不加开放性修饰词的话,默认就是public类型 |
internal | 只对本模块开放。这是Kotlin新增的关键字,对于app开发来说,本模块便指app自身 |
protected | 只对自己和子类开放 |
privated | 只对自己开放,即私有 |
3.2类的继承
Kotlin的类默认是不能继承的(即final类型),如果需要继承某类,该父类就应当声明为open类型。
open class Bird (var name:String, val sex:Int = MALE) {
//需要被继承的方法也需要加上open
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 UNKNOWN = -1
fun judgeSex(sexName:String):Int {
var sex:Int = when (sexName) {
"公","雄" -> MALE
"母","雌" -> FEMALE
else -> UNKNOWN
}
return sex
}
}
}
//继承Bird的子类Duck
class Duck(name:String="鸭子", sex:Int = Bird.MALE) : Bird(name, sex) {
}
//activity调用
btn_class_duck.setOnClickListener {
var sexBird = if (count++%3==0) Bird.MALE else Bird.FEMALE
var duck = Duck(sex=sexBird)
tv_class_inherit.text = duck.getDesc("鸟语林")
}
继承父类protected方法,标准写法是“override protected”,然而protected可忽略。示例
class Ostrich(name:String="鸵鸟", sex:Int = Bird.MALE) : Bird(name, sex) {
//protected的方法继承后可见性可升级为public,但不能降级为private
override public fun getSexName(sex:Int):String {
return if(sex==MALE) "雌" else "雄"
}
}
3.3 抽象类
abstract class Chicken(name:String, sex:Int, var voice:String) : Bird(n
ame, sex) {
...
abstract fun callOut(times:Int):String
}
子类的构造函数,原来的输入参数不用加var或val,新增的输入参数必须加入var或val,因为抽象类不能直接使用,所以构造函数不必给默认参数赋值,抽象方法必须在子类重写。
class Cock(name:String="鸡", sex:Int = Bird.MALE, voice:String="喔喔喔") : Chicken(name, sex, voice) {
override fun callOut(times: Int): String {
var count = when {
times<=0 -> 0
times>=10 -> 9
else -> times
}
return "$sexName$name${voice}叫了${numberArray[count]}声,原来它在报晓。"
}
}
tv_class_inherit.text = Cock().callOut(count++%10)//activity调用
3.4 接口
//接口不能带构造函数
interface Behavior {
//接口内部的方法默认就是抽象的,open和abstract关键字可忽略
open abstract fun fly():String
fun swim():String
//kotlin接口与Java接口不一样,允许实现方法
//该方法默认是open类型,接口的方法默认都是open类型
fun run():String {
return "大多数鸟儿跑得并不像样"
}
//Kotlin的接口允许声明抽象属性,实现该接口的类必须重载该属性
//与接口内部方法一样,抽象属性前面的open和abstract也可以忽略
var skilledSports:String
}
class Goose(name:String="鹅", sex:Int = Bird.MALE) : Bird(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 skilledSports:String = "游泳"
}
其他类实现接口时,跟类继承一样把接口名称放在冒号后面,如果存在2个以上的接口或者既有父类也有接口,此时中间用逗号隔开。
btn_interface_behavior.setOnClickListener {
tv_class_inherit.text = when (count++%3) {
0 -> Goose().fly()
1 -> Goose().swim()
else -> Goose().run()
}
}
3.5 接口代理
一个类先声明继承自某个接口,但并不直接实现该接口的方法,而是把已经实现该接口的代理类作为参数传给前面的类,相当于告诉前面的类:“该接口的方法我已经代替你实现,你直接拿去用便是。” 这样做的好处是,输入参数可以按照具体的业务场景传送相应的代理类。
示例:
//飞禽的行为类
class BehaviorFly : Behavior {
override fun fly():String {
return "翱翔天空"
}
override fun swim():String {
return "落水凤凰不如鸡"
}
override fun run():String {
return "能飞干嘛还要走"
}
override var skilledSports:String = "飞翔"
}
//水禽的行为类
class BehaviorSwim : Behavior {
override fun fly():String {
return "看情况,大雁能展翅高飞,企鹅却欲飞还休"
}
override fun swim():String {
return "怡然戏水"
}
override fun run():String {
return "赶鸭子上树"
}
override var skilledSports:String = "游泳"
}
//走禽的行为类
class BehaviorRun : Behavior {
override fun fly():String {
return "飞不起来"
}
override fun swim():String {
return "望洋兴叹"
}
override fun run():String {
return super.run()
}
override var skilledSports:String = "奔跑"
}
接着定义一个引用了代理类的野禽基类,通过关键字by表示该接口将由入参中的代理类实现。
//如果by的对象是个类,将编译报错
class WildFowl(name:String, sex:Int=MALE, behavior:Behavior) : Bird(name, sex), Behavior by behavior {
}
btn_delegate_behavior.setOnClickListener {
var fowl = when (count++%6) {
0 -> WildFowl("老鹰", Bird.MALE, BehaviorFly())
1 -> WildFowl("凤凰", behavior=BehaviorFly())
2 -> WildFowl("大雁", Bird.FEMALE, BehaviorSwim())
3 -> WildFowl("企鹅", behavior=BehaviorSwim())
4 -> WildFowl("鸵鸟", Bird.MALE, BehaviorRun())
else -> WildFowl("燕子", behavior=BehaviorRun())
}
var action = when (count%11) {
in 0..3 -> fowl.fly()
4,7,10 -> fowl.swim()
else -> fowl.run()
}
tv_class_inherit.text = "${fowl.name}$action"
}
4 几种特殊类
4.1嵌套类
在类的内部定义新类,这个新类叫作内部类。
class Tree(var treeName:String) {
class Flower (var flowerName:String) {
fun getName():String {
return "这是一朵$flowerName"
//普通的嵌套类不能访问外部类的成员,如treeName
}
}
}
btn_class_nest.setOnClickListener {
val peachBlossom = Tree.Flower("桃花");
tv_class_secret.text = peachBlossom.getName()
}
4.2 内部类
嵌套类加上inner前缀,就成为内部类
class Tree(var treeName:String) {
...
inner class Fruit (var fruitName:String) {
fun getName():String {
//只有声明了内部类,才能访问外部类的成员
return "这是${treeName}长出来的$fruitName"
}
}
}
4.3 枚举类
将关键字enum 修饰class就变成枚举类
enum class SeasonType {
SPRING, SUMMER, AUTUMN, WINTER
}
枚举类属性
ordinal属性用于获取该枚举值的序号,name属性用于获取该枚举值的名称。
枚举类如果存在构造函数,枚举变量也必须调用对应的构造函数。
enum class SeasonName (val seasonName:String) {
SPRING("春天"),
SUMMER("夏天"),
AUTUMN("秋天"),
WINTER("冬天")
}
//调用
SeasonType.SPRING.ordinal
SeasonType.SPRING.name
SeasonName.SPRING.seasonName//调用构造函数的名称
4.4 密封类
密封类像是一种更严格的枚举类,它内部有且仅有的实例对象,所以是一个有限的自身实例集合。需要在该类的class前面添加sealed作为标志
sealed class SeasonSealed {
//密封类内部的每个嵌套类都必须继承该类
class Spring (var name:String) : SeasonSealed()
class Summer (var name:String) : SeasonSealed()
class Autumn (var name:String) : SeasonSealed()
class Winter (var name:String) : SeasonSealed()
}
SeasonSealed.Spring("春天")
密封类确保条件分支覆盖了所有的枚举类型,因此不再需要else分支
tv_class_secret.text = when (season) {
is SeasonSealed.Spring -> season.name
is SeasonSealed.Summer -> season.name
is SeasonSealed.Autumn -> season.name
is SeasonSealed.Winter -> season.name
}
4.5 数据类
在class面前添加修饰词data,并声明拥有完整输入参数的构造函数,那么该类称为数据类。数据类有以下功能:
<1>自动声明与构造函数入参同名的属性字段;
<2>自动实现每个属性字段的get/set方法;
<3>自动提供equals方法,用于比较两个数据对象是否相等;
<4>自动提供copy()方法,允许完整复制某个数据对象,也可在复制后单独修改某几个字段的值;
<5>自动提供toString()方法,用于打印数据对象中保存的所有字段值
示例:
//数据类必须有主构函数,且至少有一个输入参数
//输入参数前要添加关键字var或val
//数据类不能是基类也不能是子类,不能是抽象类,也不是内部类,更不是密封类
data class Plant(var name:String, var stem:String, var leaf:String, var flower:String, var fruit:String, var seed:String) {
}
var lotus = Plant("莲", "莲藕", "莲叶", "莲花", "莲蓬", "莲子")
//数据类的copy()方法不带参数,表示复制一模一样的对象
var lotus2 = lotus.copy()
btn_class_data.setOnClickListener {
lotus2 = when (count++%2) {
0 -> lotus.copy(flower="")
else -> lotus.copy(flower="")
}
var result = if (lotus2.equals(lotus)) "相等" else "不等"
}
4.6 模板类
举例说明
//在类名后面添加“<T>”,表示这是一个模板类
class River<T> (var name:String, var length:T) {
fun getInfo():String {
var unit:String = when (length) {
is String -> ""
//Int,Long,Float,Double都是数字类型Number
is Number -> "m"
else -> ""
}
return "${name}的长度是$length$unit。"
}
}
btn_class_generic.setOnClickListener {
var river = when (count++%4) {
//模板类(泛型类)声明对象时,要在模板类的类名后面加上“<参数类型>”
0 -> River<Int>("小溪", 100)
//如果编译器根据输入参数就能知晓参数类型,也可直接忽略“<参数类型>”
1 -> River("瀑布", 99.9f)
....
}
...
}
备注:第1到第4篇kotlin基础知识汇总全部摘自《Kotlin从零到精通(Android开发)》一书。