4.1、类和接口
类的定义
1、默认是public。
2、如果类中没有内容,可以将{ } 省略。
3、类中定义属性,必须要初始化。
4、类中定义构造函数,使用constructor关键字。
5、构造器分为主构造器和副构造器,要求其它所有的构造器都必须调用它。
构造函数的几种形式
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val test5 = Test5(1)
}
}
// 只有副构造器
class Test {
var x: Int = 0
constructor(x: Int) {
this.x = x
}
}
// 包含主构造器和副构造器
class Test2 constructor(x: Int) {
var x: Int = x
var y: Int = 0
constructor(x: Int, y: Int) {
this.x = x
this.y = y
}
}
// 只有主构造器
class Test3 constructor(x: Int) {
// 赋值
var x: Int = x
}
// 将主构造器进行省略
class Test4(x: Int) {
// 赋值
var x: Int = x
}
// 继续简化:如果在构造器中增加了var/val,那么相当于上面的构造函数
class Test5(var x: Int) {
}
类的初始化(VS Java)
不需要new关键字
接口定义
interface SimpleInter {
fun test()
}
接口的实现
// 使用:表示实现接口
class Test : SimpleInter {
override fun test() {
}
}
interface SimpleInter {
fun test()
}
接口中增加属性并且实现
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val test = Test(0)
test.param = 4
Log.e("cdx", test.param.toString())
}
}
// 接口中定义了参数,这个参数不需要初始化
interface TestInterface {
var param: Int
fun test()
}
// 通过构造函数传递进来一个参数
class Test(var x: Int) : TestInterface {
// 必须要重写这个参数
override var param: Int
// 获取参数
get() {
return x
}
// 设置参数
set(value) {
this.x = value
}
override fun test() {
}
}
抽象类的定义
abstract class AbsTest {
// 表示抽象方法
abstract fun test1()
// 必须加上open方法,才能告诉编译器,可以被复写
open fun test2() {}
// 默认是不可复写
fun test3() {
}
}
抽闲类的实现
// 接口继承:必须要加上() 这个和接口的实现不一样,接口是不需要加()
class Test : AbsTest() {
override fun test1() {
}
override fun test2() {
super.test2()
}
}
继承一个普通的类要求
继承一个普通的类A,这个A类必须要是open修饰的。
一个类的方法不能被重写
一个类的方法不能被复写,这个方法增加final。
// 要想能够继承一个类,这个类必须是open的
class Test2 : Test() {
// 如果复写一个final修饰的方法,此时将会出现编译错误
// override fun test2() {
// super.test2()
// }
}
// 接口继承:必须要加上() 这个和接口的实现不一样,接口是不需要加()
open class Test : AbsTest() {
override fun test1() {
}
final override fun test2() {
super.test2()
}
}
abstract class AbsTest {
// 表示抽象方法
abstract fun test1()
// 必须加上open方法,才能告诉编译器,可以被复写
open fun test2() {}
// 默认是不可复写
fun test3() {
}
}
field使用
field定义
在get或者set方法中,表示这个属性。
field使用
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val person = Person(25, "张三")
person.age = 18
person.name = "小红"
Log.e("cdx", person.toString())
}
}
class Person(age: Int, name: String) {
var age: Int = age
get() {
// field就是指向age属性
return field
}
set(value) {
field = value
}
var name: String = name
override fun toString(): String {
return "$age,$name"
}
}
属性的引用 + 给属性的引用赋值值
属性的引用也是**::**来表示,和函数的引用是一样的。
// 这个数属性的引用,已经绑定了Receiver
val property = person::age;
property.set(1)
// 这个属性的引用,没有绑定Receiver,所以需要在设置的时候,传递对象。
val property2 = Person::age
property2.set(person, 2)
Log.e("cdx", (Person::age).toString())
4.2、扩展方法
定义:
不改变类的源码的情况下,为类增加自定义的方法。
如何实现:
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
Log.e("cdx", "abc".times(4))
}
fun String.times(times: Int): String {
var builder = StringBuilder()
for (i in 0 until times) {
// 这里的this只得调用者本身
builder.append(this)
}
return builder.toString()
}
}
4.3、空类型安全
空类型访问属性
var str: String? = null
// 使用?.来安全访问
val length = str?.length;//null
Log.e("cdx", length.toString())//null
强制转换为不可空类型
通过两个!!将可空类型变为不可空类型
// 表示是一个空类型
var str: String? = "Hello"
// 通过两个!!强转化成不为空类型
val str1 = str!!;
Log.e("cdx", str1.length.toString())
elvis运算符
// 表示是一个空类型
var str: String? = null
// elvis运算符,如果str?.length 返回null的话,那么此时返回0
val length = str?.length ?: 0
Log.e("cdx", length.toString()) //0
空类型的继承关系
小范围(String)的可以给大范围(String?) 赋值,但是大范围的不能给小范围赋值。
// 不为空
var x: String = "a"
// 可以为空(范围更大)
var y: String? = "b"
// 大范围的赋值给小范围,出现编译错误
//x = y
// 小范围的赋值给大范围,没有问题
y = x
平台类型
定义
1、在java中声明的类型,在kotlin里面会被称为平台类型。
2、这种类型的空检查会放宽。
例子
首先定义一个person类,这个是Java的类。
package com.example.kotlindemo;
public class Person {
public String name;
public String getTitle(){
return null;
}
}
title的类型居然是String!
var person = Person()
// 去使用title的时候,本质上使用的是Person的getTitle属性
val title = person.title
// 编译器不知道title是否是可空类型,所以要进行非空判断
var length = title?.length
4.4、智能类型转换
kotlin的类型转换
kotlin不需要强制转换为子类,就可以调用子类的属性。(这个和Java不太一样)
package com.example.kotlindemo
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
// 父类的引用指向子类的对象
var person = Student();
Log.e("cdx", "xxx" + person.name)
if (person is Student) {
// 不需要进行类型转换,自动调用子类的属性,下面的写法是精简的写法
Log.e("cdx", (person as Student).name)
Log.e("cdx", person.name)
}
}
}
interface Person {
fun say()
}
class Student : Person {
val name: String = "xh"
override fun say() {
}
}
作用范围
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
// 作用范围
var value: String? = null
// 这个是不行的,因为这个是可空的类型
// Log.e("cdx", value.length.toString());
if (value != null) {
// 在括号里面,由于有非空判断,所以编译器很智能的将value的类型变为了String类型,非空的类型。
Log.e("cdx", value.length.toString());
}
// 在这个地方,value的类型又变为了String可为空的类型
Log.e("cdx", value?.length.toString());
}
}
不支持智能转换的情况
class MainActivity2 : AppCompatActivity() {
var tag: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
// 这个地方理论上有非空判断,为什么不能有作用范围的概念呢?
// 因为tag是一个全局的字段,虽然判断不为空,
但是在判断为不为空的时候,
其它的线程可能将他的值修改为空,所以智能转换就不生效了
if (tag != null) {
// Log.e("cdx",tag.length.toString())
Log.e("cdx", tag?.length.toString())
}
}
}
类型的安全转换
as ?
// 安全转换:
// 如果是person是Student类型,那么就转换
// 如果不是Student类型,那么返回null
// 父类的引用指向子类的对象
var person = Student();
// 安全转换:
// 如果是person是Student类型,那么就转换
// 如果不是Student类型,那么返回null
Log.e("cdx", (person as? Student)?.name.toString())
建议
1、尽量使用val来申明不可变的引用,让程序的含义更加清晰确定。