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

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

一、类的构造


1.1 类的简单定义


首先来看看在Android中Java的MainActivity


public class MainActivity extends AppCompatActivity {
  ...
}

再看看Kotlin中的MainActivity


class MainActivity : AppCompatActivity() {
  ...
}


通过上述的代码比较,Kotlin对类的写法与Java之间有以下几点区别:

(1)Kotlin省略了关键字public,因为Kotlin默认类是开放的,所以不需要这个关键字。

(2)Kotlin用冒号“:”代替extends,也就是通过冒号表示继承关系。

(3)Kotlin进行继承时,父类后面多了括号“()”。


然后我们自己新建名为Animal的Kotlin类

步骤:

鼠标右键你的包名→New→Kotlin File/Class→创建的文件类型选择Class→OK(创建完成)


2020030309422920.png


2020030309433945.png


2020030309460046.png


然后你就会看到这样的一个图

2020030309474857.png


现在开始编写代码:

package com.llw.kotlinstart
class Animal {
    //类的初始化函数
    init {
        //Kotlin的println替换Java的System.out.println
        println("Animal:这是个动物类")
    }
}


现在这个类已经创建好了,并且有了初始化函数,我们在MainActivity.kt中来实例化这个类,代码如下:


activity_main.xml代码


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <TextView
        android:textColor="#000"
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:textColor="#000"
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:gravity="center"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/btn_test"
            android:text="Test"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</LinearLayout>


MainActivity.kt代码


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() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn_test.setOnClickListener {
            //因为根据等号后面的构造函数已经明确知道这是个Animal的实例
            //所以声明对象时可以不用指定它的类型
            var animal = Animal()
            tv_result.text = "简单类的初始化结果见日志"
        }
    }
}


运行效果图如下:


20200303095917222.png


20200303100017492.png


经过这一番操作,我们再与Java对比一下区别:

(1)Kotlin对类进行初始化的函数名称叫init,不像Java那样把雷鸣作为构造函数的名称。

(2)Kotlin打印日志使用类似C语言的println方法,而非Java的System.out.println

(3)Kotlin创建实例时省略了关键字new。

这里面,初始化函数init看似是Kotlin对类的构造函数,但它只是构造函数的一部分,并不完整,因为没有定义输入参数,那么怎么定义呢?谁来定义呢?


1.2 类的构造函数


入参的类定义代码如下:


//如果主构造函数没有带@符号的注解说明,类名后面的constructor就可以省略
    class AnimalMain constructor(context:Context,name:String){
    //class AnimalMain (context:Context,name:String){
        init {
            context.toast("这是头$name")
        }
    }


一个类可能有多个构造函数,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("这头${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("这头${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("这只${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 = "这头${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 = "这头${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 "欢迎来到$tag:这头${name}是${sexName}的"
    }
}


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


20200306162538181.png
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
    }
}


20200306162538181.png20200306162704710.png

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
6天前
|
数据安全/隐私保护 Kotlin
Kotlin - 类成员
Kotlin - 类成员
40 6
|
13天前
|
数据安全/隐私保护 Kotlin
Kotlin - 类成员
Kotlin - 类成员
34 1
|
21天前
|
Java 开发者 Kotlin
Kotlin教程笔记(2) - 类与构造器
Kotlin教程笔记(2) - 类与构造器
30 4
|
11天前
|
Kotlin
Kotlin - 枚举与密封类
Kotlin - 枚举与密封类
20 3
Kotlin - 枚举与密封类
|
5天前
|
Java Kotlin
Kotlin - 类及成员的可见性
Kotlin - 类及成员的可见性
|
8天前
|
Java Kotlin
Kotlin - 类及成员的可见性
Kotlin - 类及成员的可见性
23 5
|
6天前
|
数据安全/隐私保护 Kotlin
Kotlin - 类成员
Kotlin - 类成员
16 2
|
9天前
|
Kotlin
Kotlin教程笔记(20) - 枚举与密封类
Kotlin教程笔记(20) - 枚举与密封类
23 3
|
11天前
|
存储 前端开发 Java
Kotlin - 数据类
Kotlin - 数据类
|
14天前
|
Java 开发者 Kotlin
Kotlin教程笔记(2) - 类与构造器
Kotlin教程笔记(2) - 类与构造器
34 6