7.1、类的构造器
1、构造器的基本写法
/**
* 1、constructor 可以省略。
* 2、加上了val或者var,定义了构造器参数,并且定义了属性。
* 3、带val或者var的,那么类内可见,如果不带,那么init块内可见。
* 4、如果带了public,那么类外可见。
*/
class Person constructor(val name: String, age: Int) {
}
2、init块
class Person constructor(val name: String, age: Int) {
private val TAG: String = "Person"
var age:Int
/**
* 1、init块类似于主构造器的方法体。
* 2、init块可以有多个。
* 3、init块中可以访问构造器中的参数。
*/
init {
this.age = age
}
init {
Log.e(TAG,"1")
}
}
思考:init块会合并吗?
init块会合并,最终会合并到构造器的方法体。
3、属性必须初始化
class Person constructor(val name: String, age: Int) {
private val TAG: String = "Person"
var age:Int //属性必须初始化,要不会编译错误
init {
//this.age = age
}
}
4、继承父类
abstract class Animal
class Person constructor(val name: String, age: Int):Animal(){
}
5、副构造器
package com.example.kotlindemo
abstract class Animal
class Person constructor(val name: String, age: Int) : Animal() {
/**
* 1、定义在类内部的构造器称为副构造器。
* 2、副构造器必须要调用主构造器
* 3、如果不调用主构造器,会出现编译错误。
*/
constructor(age: Int): this("", age){
}
}
6、不定义主构造器(不推荐)
package com.example.kotlindemo
abstract class Animal
/**
* 注意:此时没有()
*/
class Person : Animal {
/**
* 1、通过super()调用父类构造器。
* 2、如果父类构造器为无参,比如super(),可以省略。
* 3、如果有多个构造器,可以调用其它的构造器。
*/
constructor(age: Int) : super() {
}
constructor(age: Int, name: String) : this(age) {
}
}
7、主构造器默认参数(推荐)
/**
* 主构造器默认参数
*/
class Person(val age: Int, val name: String = "") : Animal() {
}
8、构造同名的工厂函数
第一步:定义Person类。
abstract class Animal
/**
* 主构造器默认参数
*/
class Person(val age: Int, val name: String = "") : Animal() {
}
第二步:定义同名的工厂函数。
class MainActivity : AppCompatActivity() {
val persons = HashMap<String, Person>()
/**
* 同名的工厂函数,当然也可以起其他的名字
*/
fun Person(name: String): Person {
return persons[name] ?: Person(1, name).also { persons[name] = it }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
7.2、类与成员的可见性
1、可见性对比
2、修饰对象
3、模块的概念
大致可以认为是一个jar包或者是一个aar。
4、internal VS default
一般用在SDK开发或者公共组件的开发上,用于隐藏模块内部的实现细节。
kotlin中为了能让外界对于对于内部的实现是不知道的,可以用internal修饰,非常的方便。
java中为了不让外界访问,可以用default访问,但是这样就会导致这些类必须放在一个包下,难以维护。
5、构造器的可见性
package com.example.kotlindemo
abstract class Animal
/**
* 1、为了构造一个单例。
* 2、或者利用工厂方法生成对象。
* 那么此时可以在构造方法前面增加private
*/
class Person private constructor(val age: Int, val name: String = "") : Animal() {
}
6、属性的可见性
package com.example.kotlindemo
abstract class Animal
/**
* 属性的可见性
* 属性增加private 外界无法访问
*/
class Person(private val age: Int, val name: String = "") : Animal() {
}
7、顶级声明的可见性
1、顶级声明指文件内部定义的属性、函数、类等。
2、顶级声明不支持protected。
3、顶级声明被private修饰表示文件内可见。
7.3、类属性的延迟初始化
1、为什么要延迟初始化?
1、类属性必须在构造时初始化。
2、某些成员只有在类构造之后才会被初始化。
比如Activity中的UI。
2、类属性如何处理?
1、初始化为null (不推荐)
private var name:TextView? = null
2、使用lateinit(稍微推荐)
private lateint var name:TextView;
在后面延迟初始化,注意:必须要用var。
3、使用lazy(非常推荐)
class MainActivity : AppCompatActivity() {
// 注意:只有在name在首次访问的时候被执行。
private val name by lazy {
findViewById<TextView>(R.id.tv)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
7.4、代理
1、接口代理。
package com.example.kotlindemo
import android.util.Log
//第一步:定义一个接口
interface Api {
fun a()
fun b()
fun c()
}
//第二步:定义一个接口实现类
class ApiImpl:Api{
override fun a() {
}
override fun b() {
}
override fun c() {
}
}
// 第三步:定义一个类,用于api功能的增强
class ApiWrapper(val api:Api):Api{
override fun a() {
api.a()
}
override fun b() {
api.b()
}
override fun c() {
// 比如增加一些日志
api.c()
}
}
// 针对第三步,可以有简单的写法
// 对象api代替类ApiWrapper实现接口Api
// 相当于通过by api 将实现交给了api来实现
// 对象api的要求就是实现被代理的接口
class ApiWrapper2(val api:Api):Api by api{
// 对功能进行增强
override fun c() {
Log.e("test","test")
api.c()
}
}
2、属性代理 lazy
package com.example.kotlindemo
class Person(val name:String) {
// lazy 代理了firstName 当第一次调用firstName的时候,回去获取lazy返回的值,也就是lambda的表达式的值
val firstName by lazy {
name.split(" ")[0]
}
}
3、属性代理 observable
第一步:定义属性代理。
class StateManager {
private val TAG = StateManager::class.simpleName
// state 属性被Delegates.observable属性代理。
var state: Int by Delegates.observable(0) { property, oldValue, newValue ->
Log.e(TAG, "property:" + property + ",oldValue:" + oldValue + ",newValue:" + newValue)
}
}
第二步:在MainActivity中调用它。
val stateManager = StateManager()
stateManager.state = 5;
第三步:得到的日志如下
property:property state (Kotlin reflection is not available),oldValue:0,newValue:5
4、属性代理的例子
package com.example.kotlindemo
import kotlin.reflect.KProperty
class Foo {
// 属性x被X()代理,
// 当读取x属性的时候,回去执行X()的getValue方法
// 当读取y属性的时候,会去执行X()的setValue方法
val x: Int by X()
var y: Int by X()
}
class X {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return 2
}
operator fun setValue(foo: Any, property: KProperty<*>, i: Int) {
}
}
7.5、利用属性代理读取Properties文件
比如有一个文件
Config.properties文件
author=zhangsan
version=1.0
desc=This is a demo
import java.io.File
import java.io.FileInputStream
import java.net.URL
import java.util.*
import kotlin.reflect.KProperty
class PropertiesDelegate(private val path: String, private val defaultValue: String = "") {
private lateinit var url: URL
private val properties: Properties by lazy {
val prop = Properties()
url = try {
javaClass.getResourceAsStream(path).use {
prop.load(it)
}
javaClass.getResource(path)
} catch (e: Exception) {
FileInputStream(path).use {
prop.load(it)
}
URL("file://${File(path).canonicalPath}")
}
prop
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return properties.getProperty(property.name, defaultValue)
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
properties.setProperty(property.name, value)
File(url.toURI()).outputStream().use {
properties.store(it, "Hey!!")
}
}
}
abstract class AbsProperties(path: String) {
protected val prop = PropertiesDelegate(path)
}
class Config : AbsProperties("Config.properties") {
var author by prop
var version by prop
var desc by prop
override fun toString(): String {
return """
author:$author,
version:$version,
desc:$desc
""".trimIndent()
}
}
fun main() {
val config = Config()
println(config.toString())
config.version = "1.1"
println(config.toString())
}
7.6、单例
1、kotlin中的单例
kotlin中单例如下表示:
package com.example.demo
/**
* 单例
*/
object Singleton {
var x:Int = 2
fun y(){
}
}
java中如何访问这个单例
int x = Singleton.INSTANCE.getX();
kotlin中如何访问这个单例
val x = Singleton.x
2、静态成员@JvmStatic
在kotlin中定义用@JvmStatic修饰的属性
object Singleton{ @JvmStatic var x:Int = 2}
在Java中可以进行如下访问
int x = Singleton.getX();
3、不生成getter/setter @JvmField
在kotlin中定义如下
object Singleton{
@JvmField var x:Int = 2
}
在java中访问
int x = Singleton.x;
4、普通类的静态成员
普通类使用@JvmStatic会出现编译错误
class Foo {
// 会出现编译错误
@JvmStatic fun y()
}
要想在普通类上使用静态成员,可以使用伴生对象
class Foo {
companion object{
@JvmStatic fun y() {
}
}
}
在Java中调用,类似于静态方法调用
Foo.y();
在kotlin中调用
Foo.y()
5、object 的构造器
默认有一个无参的构造器
6、object 的类继承(和Java中一致)
object Test:Runnable{
override fun run() {
}
}
7.7、内部类
1、内部类定义
1、和Java的定义相反,在kotlin中,加上inner表示非静态,什么都不加,表示静态。
2、内部类实例化
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
// 非静态内部类的调用
val inner = Outer().Inner()
// 静态内部类的调用
val staticInner = Outer.StaticInner()
}
}
class Outer {
// 非静态内部类
inner class Inner
// 静态内部类
class StaticInner
}
3、内部object
// 由于object内部成员默认是static的,
// 所以不能再内部的object前面增加Inner
object OuterObject {
object StaticInnerObject
}
4、匿名内部类
java和kotlin匿名内部类的表示
5、匿名内部类实现多个接口
java并不支持,但是kotlin确支持
7.8、数据类
1、数据类component属性
因为是数据类,所以才有这个特性。
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
var data = TestData("张三", 11)
val component1 = data.component1()
val component2 = data.component2();
Log.e("cdx","$component1-$component2")
}
}
data class TestData(val name: String, val age: Int)
2、data类的特性
1、有component方法。
2、编译器基于component自动生成了equals/hashCode/toString/copy。
3、数据类解构
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
var data = TestData("张三", 11)
// 解构赋值
var (name, age) = data
Log.e("cdx", "$name-$age")
}
}
4、数据类 java和kotlin的区别
7.9、枚举类
1、Java和kotlin枚举的区别
2、枚举的属性
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
Log.e("cdx",State.Idle.name)
Log.e("cdx",State.Busy.name)
}
}
enum class State {
Idle, Busy
}
结果如下:
3、枚举的构造器
4、枚举的接口
5、枚举的接口-各自实现
6、枚举进行扩展(和Java不同的地方)
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
Log.e("cdx", State.Idle.next().name)
}
// 获取下一个美剧值
fun State.next(): State {
return State.values().let {
val nextOrdinal = (ordinal + 1) % it.size
it[nextOrdinal]
}
}
}
7、枚举用来比大小
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
var state = State.Idle
if (state < State.Idle){
Log.e("cdx","11111")
} else if (state < State.Busy){
Log.e("cdx","22222")
}
}
}
enum class State {
Idle, Busy
}
8、枚举的区间
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
var colors = Color.Yello..Color.Green
var color = Color.Black
var result = color in colors
}
}
enum class Color {
Yello, Black, Red, Green, White, Gray
}
7.10、密封类
1、定义
1、密封类是一种特殊的抽象类。
2、密封类的子类定义在与自身相同的文件中。
3、密封类的子类的个数是有限的。
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val state: PlayerState = Playing()
val result = when (state) {
Idle -> {
1
}
is Playing -> {
2
}
is Error -> {
3
}
else -> {
4
}
}
}
}
//1、首先是一个抽象类
//2、其次是一个密封类
sealed class PlayerState {
constructor()
constructor(int: Int)
}
//单例
object Idle : PlayerState() {}
class Playing(val song: String = "") : PlayerState() {}
class ErrorInfo(val error: Int = 0) : PlayerState() {}
2、密封类VS枚举类
7.11、内联类
1、定义
1、内联类是对某一个类型的包装。
2、内联类是类似于Java装箱类型的一种类型。
3、编译器会尽可能使用被包装的类型进行优化。
/**
* 1、对Int类型的包装
* 2、必须是val。
*/
inline class BoxInt(val value:Int){
operator fun inc():BoxInt{
return BoxInt(value + 1)
}
}
2、内联类的属性
内联类是对其它类型的包装,所以不应该有属性。
3、内联类的继承结构
1、可以实现接口,但是不能继承父类也不能被父类继承。
4、内联类的编译优化
内联类的方法都会被编译成静态方法
5、内联类的使用场景
6、内联类的限制
1、主构造器必须有且仅有一个只读属性。
2、不能定义有backing-field的其他属性。
3、被包装类型必须不能是泛型类型。
4、不能继承父类也不能被继承。
5、内联类不能定义为其他类的内部类。
7.12、kotlin中的JSON序列化
Gson:JSON解析库
kotlinx.serialization:JSON解析库
moshi:JSON解析库