// 某些成员变量在声明时无法初始化,且不想使用可空类型(也就是带?的类型)。可使用 lateinit 和 by lazy
// 不知道具体值,后面再赋值使用;所以必须var
lateinit var name: String
// 知道具体值,用到的时候再加载,表示不可变的值,所以必须val
// {}中的最后一行代码返回初始化的结果,加载时机为第一次调用常量的时候,且只会加载一次
private val book by lazy {
LogUtil.i("这行代码不会影响book赋值")
"Android"
}
public: 公开,可见性最大,哪里可以引用 Kotlin 的类默认是 public 的
private: 私有,可见性最小,根据声明位置不同可分为类中可见和文件中可见
protected: 保护,相当于private+ , 子类可见
internal: 内部,仅对module内可见,(A module修饰,B module无法访问)
/**
* 构造函数单独用了一个 constructor 关键字来和其他的 fun 做区分,分主构造和次构造
* 如果类中有主构造器,所有次构造器都需要通过this关键字调用主构造器
*
* 从主构造器的特性出发,一旦在类中声明了主构造器,就包含两点:
* 必须性:创建类的对象时,不管使用哪个构造器,都需要主构造器的参与
* 第一性:在类的初始化过程中,首先执行的就是主构造器
*/
class User constructor(var name: String) {
// init代码块紧跟主构造器之后,因为主构造函数没有代码体,init代码块可充当主构造器代码体的功能; init代码块优先于次构造器执行的。
init {
// 如果把主构造器看成身体的头部,那么 init 代码块就是颈部,次构造器就相当于身体其余部分
}
// 直接调用主构造器
constructor(name: String, age: Int) : this(name)
// 通过上一个次构造函数,间接调用主构造器
constructor(name: String, age: Int, id: Int) : this(name, age) {}
}
// 主构造器中 constructor 关键字如果没有被 可见性修饰符 或 注释 标注可以省略
class User(var name: String) {
constructor(name: String, age: Int) : this(name)
}
// 主构造器被修饰为私有的,外部就无法调用该构造器
class User private constructor(var name: String) {
}
// 主构造函数里面声明属性,前面加 var / val 构造参数同时成为成员变量
class User(var name: String) {
}
// ↑↓ 上下等价
class User(name: String) {
var name: String = name
}
一共三种实现方式:①object ②伴生对象 ③顶层函数
/**
* Kotlin的object首字母小写,Java的Object在Kotlin中不用了,Java的Object在Kotlin中变成了Any
* 在 Kotlin 中object不是类属于关键字
*
* 用object声明一个类时,表明这个类是一个单例类,可继承其他类,可以定义在全局也可以在类的内部使用
* object声明不能用在方法和inner内部类中,但是能嵌套在其他object声明和嵌套类中
* object 定义后即刻实例化,因此 object 不能有定义构造函数
*/
object SystemUtil{
}
/**
* 用 object 修饰的对象中的变量和函数都是静态的,但有时候只想让类中的一部分函数和变量是静态的
* 和 object 不同, companion object 的定义完全属于类的本身,
* 因此 companion object 不能脱离类而定义在全局之中。
* 类似 Java 里的 static 变量
*/
class ScreenUtil {
/*
* companion object 修饰为伴生对象,伴生对象在类中只能存在一个
* Java 中的静态变量和方法,在 Kotlin 中都放在了 companion object 中。
* 因此 Java 中的静态初始化在 Kotlin 中自然也是放在 companion object 中的,
* 像类的初始化代码一样,由 init 和一对大括号表示
*/
companion object {
// 静态初始化
init { }
}
}
真实项目中推荐的写法是使用伴生对象和 object
关键字结合的方式,示例如下:
class UmengEventUtil {
companion object {
@JvmStatic
fun getInstance(): UmengEventUtil {
return Holder.instance
}
}
private object Holder {
val instance = UmengEventUtil()
}
fun onRegister(mContext: Context) {
MobclickAgent.onEvent(mContext, "__cust_event_1")
}
}
顶层声明 top - level property / function
又称顶层函数,直接在文件中定义函数和属性,这种顶层函数不要声明在module内最顶层的包中,至少要在一个包中
/*
顶层声明属于 package,不在 class/object 内
属性和函数,不属于任何 class,而是直接属于 package
它和静态变量、静态函数一样是全局的,用起来更方便:在其它地方用的时候,类名都不用写
*/
fun stringToDouble(money: String): Double {
return money.toDouble()
}
// 默认kotlin已自动生产set,get方法,可省略
class User() {
var name: String? = null
set(value) {
field = value
}
get() = field
}
// 上下等价 ↑↓
class User{
var name: String? = null
}
// 如只想让外部使用,不想外部set,可在set前面添加private将set方法私有化
class User {
var name: String
private set
init {
name = "设置name值,外部只能调用get方法"
}
}
相比JavaBean手动生成各种方法,kotlin data class要简单很多
/**
* var user = User("zs",18,"10001")
* 自动生成
* toString() LogUtil.e("user.toString = $user")
* copy() var user2 = user.copy(id = 2)
* equals() user.equals(user2) 输出 true
* hashCode() user.hashCode 输出内存地址
* componentN() 解构声明 val (name, age, code) = User("zs", 0, "1001")
*/
data class User(
var name: String,
var age: Int,
var code: String
)
// 原本使用
fun dp2px(dp: Float): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().displayMetrics)
}
dp2px(3.4f)
// 扩展函数
fun Float.dp2px(): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)
}
3.2f.dp2px()
// 使用let之前
tvName.text = user?.name
tvAge.text = user?.age
tvCode.text = user?.code
// let 配合可空性 “?”使用,如果user为 null 不执行 let 内部代码,如果有返回值返回null,在 let 中 it 表示引用对象
user?.let {
tvName.text = it.name
tvAge.text = it.age
tvCode.text = it.code
}
val user = User()
tvName.text = user.name
tvAge.text = user.age
tvCode.text = user.code
// with 函数适用于对同一个对象执行多次操作
with(user) {
tvName.text = name
tvAge.text = age
tvCode.text = code
}
// with 函数最后一行为返回值
val nickName = with(user) {
name
}
// 适用于同一个类多个方法省去类名重复,直接调用类中方法或属性,在RecyclerView绑定数据时经常使用
/*
run函数是let、with两个函数结合体,
它弥补了let函数在函数体内必须使用it参数替代对象,
在run函数中可以像with函数一样,直接访问实例的公有属性和方法
另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理
*/
user?.run {
tvName.text = name
tvAge.text = age
tvCode.text = code
}
// 适用于let、with函数任何场景
// 使用方式和run一致,但是不同的是:最后一行不作为返回值,apply函数的返回值是本身,在函数内部可任意调用对象属性和方法,甚至给属性赋值等
val fragmentList = ArrayList().apply {
add(HomeFragment.newInstance())
add(OrderFragment.newInstance())
add(UserFragment.newInstance())
}
// 1:枚举最常用的方法:定义枚举常量,使用逗号隔开,每个枚举常量都是一个对象
enum class Color {
RED,
GREEN,
BLUE
}
// 2:定义枚举常量时,为其声明一个成员变量,可以为其初始化一个值
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}