参考资料
Kotlin for android Developers 中文翻译
环境搭建
使用 IDEA 开发:
1 Kotlin 基础语法
1.1 变量声明、函数声明
package chapter1
/**
* 变量名在前,变量类型在后,如果类型能推断可以不用写类型
*/
//var 表示变量
var age: Int = 28
//val 表示不可变的变量。注意:不是常量
val name: String = "huangjian"
//如果类型可以推断出来,可以不用写类型
var sex = "male"
var str1: String = "test"
//String 和 String? 是两种不同的类型。是不能互相赋值的
//String 类型是不可为空的,如果设置为空编译器会报错
//String? 是可以为空也可以不为空的类型
var str2: String? = "test2"
fun main(args: Array<String>) {
//不能直接赋值,因为是两种不同的类型,如果能保证 str2 不为空,可以加 !! 进行强转赋值
str1 = str2!!
//可以直接赋值,因为 String? 表示可以为空和不可为空的类型,其中包含 String
str2 = str1
//入参不能是 String? 类型
// pringLn(str2)
pringLn(str1)
}
/**
* 函数以关键字 fun 标识
* 1、函数参数声明和变量声明一致
* 2、函数返回值在函数体后声明
*/
fun pringLn(arg: String): String {
//Kotlin 的模板语法:可以使用 $ 在字符串中引用变量
println("字符串为:$arg")
return arg
}
1.2 Kotlin 和 Java 互调
Idea 定义 Java 代码生成 main 函数模板:
intellij idea优化_生成main方法_自动补全代码_live template
JavaMain.java 文件
package chapter1.class2;
public class JavaMain {
//测试与 Kotlin 关键字冲突问题
public static String in = "in";
public static void main(String[] args){
//调用 Kotlin 中的函数,直接通过 “文件名Kt.方法名”调用
//Util 中声明的方法、变量 ,最终都会被编译成 public
UtilKt.echo(UtilKt.getName());
//调用匿名内部类
Test.INSTANCE.sayMsg("hello from main");
}
}
Util.kt 文件
package chapter1.class2
import kotlin.reflect.KClass
//最终被编译成 public 变量
var name = "huangjian"
/**
* Kotlin 的函数直接可以写在文件中,不用写在类中
* 最终被编译成 public 的方法,在 Java 类中可以直接通过文件名调用该方法
*/
fun echo(name: String) {
println("$name")
}
fun main(args: Array<String>) {
//Kotlin 中可以直接调用匿名类的方法
Test.sayMsg("hello from Kotlin")
}
/**
* 匿名内部类的写法
*/
object Test {
fun sayMsg(msg: String) {
println(msg)
//java 的 class 对象传入方式
testClass(JavaMain::class.java)
//kotlin 的 class 对象传入方式
testClass(KotlinMain::class)
//kotlin 掉 Java 变量,变量名与 kotlin 的关键字冲突时使用 ``
println(JavaMain.`in`)
}
}
/**
* 测试入参为 Java 的 Class
*/
fun testClass(clazz: Class<JavaMain>) {
println(clazz.simpleName)
}
/**
* 测试入参为 Kotlin 的 Class。Kotlin 的 Class 为 KClass
*/
fun testClass(clazz: KClass<KotlinMain>) {
println(clazz.simpleName)
}
KotlinMain.kt 文件
package chapter1.class2
class KotlinMain
1.3 Kotlin 与 Java 的不同
Kotlin 没有封装类,所有的变量最终都被解释为 Java 中的基本数据类型,而不是封装类型
TestInterface.java 文件
package chapter1.class3;
public interface TestInterface {
//入参为基本数据类型
void putNumber(int number);
//入参为封装类型
void putNumber(Integer number);
}
Test.java 文件
package chapter1.class3;
public class Test implements TestInterface {
@Override
public void putNumber(int number) {
System.out.println("int 类型的调用");
}
@Override
public void putNumber(Integer number) {
System.out.println("Integer 类型的调用");
}
}
Test.kt 文件
package chapter1.class3
fun main(args: Array<String>) {
//调用 Java 中 Test 类的方法,调用的是入参为 int 类型的方法,不是入参为 Integer 的方法
//如果想要调用 入参为 Integer 类型的方法,需要使用反射才能调用
Test().putNumber(123)//输出为“int 类型的调用”
}
class TestInKotlin : TestInterface {
override fun putNumber(number: Int) {
}
//编译器会提示没有 Integer 这个类型的
// override fun putNumber(number: Int?) {
// }
}
Kotlin 类型没有空值敏感
Test.java 文件
package chapter1.class3;
public class Test{
//声明静态方法
public static String fromat(String string) {
return string.isEmpty() ? null : string;
}
}
Test.kt 文件
package chapter1.class3
fun main(args: Array<String>) {
function("")
}
fun function(str: String) {
//编译器类型推断为 String! ,这个类型是编译器临时使用的,不能手动的声明
val fmat1 = Test.fromat(str)
//会报错,因为范围值为 null 不是 String 类型
// val fmat2: String = Test.fromat(str)
val fmat3: String? = Test.fromat(str)
//会报空指针错误,因为 fmat1 可能为 null
// println(fmat1.length)
//Kotlin 的特性,空安全
println(fmat3?.length)//输出为 null
}
Kotlin 没有静态变量和静态方法,声明静态方法和静态变量的方法如下:
Test.kt 文件
package chapter1.class3
object KotlinTest {
//使用 @JvmStatic 注解,将 Kotlin 中方法声明为静态变量
@JvmStatic
val str: String = "str from KotlinTest "
//使用 @JvmStatic 注解,将 Kotlin 中方法声明为静态方法,在 Java 可以直接通过“类名.方法名”调用
@JvmStatic
fun print(str: String) {
println(str)
}
}
Test.java 文件
package chapter1.class3;
public class Test {
public static void main(String[] args) {
//调用 Kotlin 中的静态方法和静态变量
KotlinTest.print(KotlinTest.getStr());
}
}
1.4 Kotlin 中的函数
函数和方法的区别(Java 中只有方法,没有函数):
Kotlin.kt 文件
package chapter1.class4
fun echo(name: String) {
println("$name")
}
/**
* 带默认值的函数,可大大减少方法重载的数量
*/
fun echo2(name: String = "huangjian") {
println(name)
}
/**
* 函数体只有一个语句时,可以直接赋值给函数
*/
fun echo3(name: String) = println("$name")
/**
* 函数嵌套。不建议使用
* 递归调用时,或者不希望函数被外部访问时可以使用内部函数
*/
fun function() {
val str = "hello world"
fun say(count: Int = 10) {
println(str + count)
if (count > 1) {
say(count - 1)
}
}
say()
}
fun main(args: Array<String>) {
echo("hello")
echo2()
echo2("hello2")
echo3("hello3")
function()
}
Kotlin 中的扩展函数,用于一些第三方的 SDK,或者不可更改的类中新增方法时,如下:
Test.kt 文件
package chapter1.class5
import java.io.File
import java.nio.charset.Charset
/**
* Kotlin 独特的扩展函数
* 静态的给类添加成员变量和成员方法
*/
fun File.readText(charset: Charset = Charsets.UTF_8): String = readBytes().toString(charset)
fun main(args: Array<String>) {
val file = File("KotlinIDEA.iml")
println(file.readText())
}
JavaMain.java 文件
package chapter1.class5;
import kotlin.text.Charsets;
import java.io.File;
public class JavaMain {
public static void main(String[] args){
File file = new File("KotlinIDEA.iml");
//Java 中调用 Kotlin 中的扩展函数,注意需要将扩展的对象作为第一个入参传入,而且没有默认的入参值
String result = TestKt.readText(file, Charsets.UTF_8);
System.out.println(result);
}
}
Kotlin 扩展函数静态解析,没有运行时多态
Test.kt 文件
package chapter1.class5
/**
* Kotlin 中的类默认加上 final 关键字
* open 表示这个类不是 final 的
*/
open class Animal
class Dog : Animal()
//给 Animal 新增静态方法
fun Animal.name() = "animal"
//给 Dog 新增静态方法
fun Dog.name() = "dog"
//给 Animal 新增静态方法
fun Animal.printName(animal: Animal) {
println(animal.name())
}
//给 Dog 新增静态方法
fun Dog.printName2(animal: Animal) {
println(animal.name())
}
//给 Dog 新增静态方法
fun Dog.printName3(dog: Dog) {
println(dog.name())
}
fun main(args: Array<String>) {
/**
* 扩展函数静态解析,没有运行时多态
*/
Dog().printName(Dog())//输出“animal”
Dog().printName2(Dog())//输出“animal”
Dog().printName3(Dog())//输出“dog”
}
1.5 Kotlin 中 Lambda
java 8 中的 Lambda 表达式:
JavaMain.java 文件
package chapter1.class7;
public class JavaMain {
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());//main
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());//Thread-0
}
});
thread.start();
//java 8 Lambda 表达式
Thread thread1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName());//Thread-1
});
thread1.start();
}
}
Kotlin 中的 Lambda:
KotlinMain.kt 文件
package chapter1.class7
fun main(args: Array<String>) {
testLambda()
echo("hello")
lambdaA(1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1)
lambdaB(1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1)
}
fun testLambda() {
Thread({ -> println(Thread.currentThread().name) }).start()
//Lambda 没有参数可以省略箭头
Thread({ println(Thread.currentThread().name) }).start()
//Lambda 是函数的最后一个参数,可以将大括号放在小括号外边
Thread() { println(Thread.currentThread().name) }.start()
//函数只有一个参数且是 Lambda 可以省略小括号
Thread { println(Thread.currentThread().name) }.start()
}
//Lambda 闭包声明
val echo = { name: String -> println(name) }
val lambdaA = { a: Int, b: Int, c: Int, d: Int, e: Int,
f: Int, g: Int, h: Int, i: Int, j: Int,
k: Int, l: Int, m: Int, n: Int, o: Int,
p: Int, q: Int, r: Int, s: Int, t: Int,
u: Int, v: Int ->
println("huangjian")
}
//Lambda 闭包最多只有22个参数,否则会报错:java.lang.NoClassDefFoundError: kotlin/Function23
//解决方案:使用 Java 代码手动定义 Function23。需要在 kotlin 包下使用 java 代码手动实现
//使用 kotlin 实现会报错:Error:(1, 1) Kotlin: Only the Kotlin standard library is allowed to use the 'kotlin' package
val lambdaB = { a: Int, b: Int, c: Int, d: Int, e: Int,
f: Int, g: Int, h: Int, i: Int, j: Int,
k: Int, l: Int, m: Int, n: Int, o: Int,
p: Int, q: Int, r: Int, s: Int, t: Int,
u: Int, v: Int, w: Int ->
println("huangjian")
}
Function23.java 文件
package kotlin;
/**
* A function that takes 23 arguments.
*/
public interface Function23<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11,
P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, R> extends Function<R> {
R invoke(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9,
P10 p10, P11 p11, P12 p12, P13 p13, P14 p14, P15 p15, P16 p16,
P17 p17, P18 p18, P19 p19, P20 p20, P21 p21, P22 p22, P23 p23);
}
高阶函数的使用:
KotlinMain.kt 文件
package chapter1.class8
fun main(args: Array<String>) {
//最后一个参数为 Lambda 可以将大括号放到小括号的拿出来
onlyIf(true) {
println("hello 高阶函数")
}
//最后一个参数需要传入为函数的声明
onlyIf(true, function)
val button: View = View()
button.id = 123123
button.setOnClick(View.OnClickListener { println("开始设置 listener1") })
button.setOnClick2 { view: View? ->
println(view?.id)
println("开始设置 listener2")
}
button.setOnClick2 {
//Lambda 表达式单个参数会隐式声明为 it 。可以不用声明唯一的参数并忽略 ->,通过 it 来调用
println(it?.id)
println("开始设置 listener2")
}
button.setOnClick3 {
println(it?.id)
println("开始设置 listener3")
}
}
/**
* 函数的参数是函数(Lambda)
* 函数最后一个参数:参数为空 返回值为 Unit 的 Lambda
* Unit 是一个没有返回值的函数默认会返回的类型,一般可以省略
* 这里 Unit 不能省略:https://www.kotlincn.net/docs/reference/lambdas.html#函数类型
*/
/**
* Kotlin 中,Lambda 会被编译成一个匿名对象
* 使用 inline 关键字(内联)可以优化,编译时不会生成额外的对象,调用处直接回拆解方法的调用为语句的调用
* 从而可以减少创建不必要的对象(以通过源码查看调用处)
*/
inline fun onlyIf(isDebug: Boolean, block: () -> Unit) {
if (isDebug) block()
}
//声明一个 Runnable 对象
val runnable = Runnable { println("Runnable::run") }
//函数是“一等公民”,可以作为一个变量。声明一个函数:参数为空,返回值为 Unit
// 引用函数声明:对象::函数名,可以作为参数传递给高阶函数 。不能是 对象.函数 ,这样实际调用的是函数的返回值
val function: () -> Unit = runnable::run
/**
* 模拟设置 View 的 listener
* 参数为:View.OnClickListener 对象
*/
fun View.setOnClick(listener: View.OnClickListener) {
setOnClickListener(listener)
}
/**
* 模拟设置 View 的 listener
* 参数为: Lambda 表达式。
*/
fun View.setOnClick2(listener: (view: View?) -> Unit) {
setOnClickListener(listener)
}
/**
* 模拟设置 View 的 listener
* 参数为: Lambda 表达式 .可以省略闭包参数名,直接声明类型即可
*/
fun View.setOnClick3(listener: (View?) -> Unit) {
setOnClickListener(listener)
}
View.java 文件
package chapter1.class7;
import org.jetbrains.annotations.Nullable;
public class View {
/**
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*/
public void setOnClickListener(@Nullable OnClickListener l) {
System.out.println("clicklistener 被调用了");
}
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
}
1.6 Kotlin 类相关知识
1.7 Kotlin 伴生对象、单例相关
可见性修饰符:
可见性修饰符 internal 标识的是在模块内可见(在项目中创建的 Module)
伴生对象及单例的写法:
KotlinMain.kt 文件
package chapter1.class10
fun main(args: Array<String>) {
println(StringUtils.isEmpty("="))
println(StringUtils2.isEmpty(""))
Single.getInstance().testInstance()
}
//静态函数的第一种写法。
object StringUtils {
@JvmStatic
fun isEmpty(string: String?): Boolean {
if (string == null) {
return true
} else {
return "" == string
}
}
}
//静态函数的第二种写法
class StringUtils2 {
//伴生对象。编译后的源码可知,会在 StringUtils2 的内部生成一个 Companion 静态类的对象。
//因此 java 调用的方式是:StringUtils2.Companion.isEmpty("")
companion object {
fun isEmpty(string: String?): Boolean {
if (string == null) {
return true
} else {
return "" == string
}
}
}
}
//单例模式:直接使用 object 或者 使用伴生对象
//伴生对象写法(推荐)
//需要私有化构造函数
class Single private constructor() {
private object Holder {
val instance = Single()
}
companion object {
fun getInstance(): Single = Holder.instance
}
fun testInstance() {
println("测试单例模式")
}
}
JavaMain.java 文件
package chapter1.class10;
public class JavaMain {
public static void main(String[] args) {
System.out.println(StringUtils.isEmpty(""));
test();
}
//java 调用 kotlin 伴生对象
public static void test() {
System.out.println(StringUtils2.Companion.isEmpty(""));
}
}
1.8 动态代理 及 特殊类
Java 中动态代理:
Kotlin 中动态代理:
Animal.kt 文件
package chapter2.class11
interface Animal{
fun bark()
}
class Dog : Animal {
override fun bark() {
println("狗叫")
}
}
//Kotlin 动态代理会被转换成静态代理,比 Java(最终通过反射) 效率高
class Zoo(animal: Animal) : Animal by animal{
// override fun bark() {
// println("Zoo bark")
// }
}
DataClass.kt 文件
package chapter2.class11
//数据类是 final 的,不能使用 open 修饰,不能被继承
//默认添加 getter/setter 方法,默认实现 toString 、equals、copy 等方法
//查看生成的源码可以了解更多
data class User(val id: Int, val name: String)
KotlinMain.kt 文件
package chapter2.class11
fun main(args: Array<String>) {
Zoo(Dog()).bark()
val user = User(3123, "huangjian")
val user2 = User(3123, "huangjian")
val user3 = user.copy()
println(user.name)
println(user.toString())
println("user == user2:${user == user2}")
println("user === user2: ${user === user2}")
println("user == user3: ${user == user3}")
println("user === user3: ${user === user3}")
}
JavaMain.java 文件
package chapter2.class11;
public class JavaMain {
public static void main(String[] args){
//Java 动态代理实现见 study.proxy 包 和 study.proxyretrofit 包下相关 demo
}
}
SealedClass.kt 文件
package chapter2.class11
//密闭类:所有的子类都在一起声明
sealed class SuperCommand {
object A : SuperCommand()
object B : SuperCommand()
object C : SuperCommand()
object D : SuperCommand()
//可对枚举类进行扩展
class E(val id: Int) : SuperCommand()
}
2.1 Kotlin 中的解构特性
代码如下:
package chapter2.class13
fun main(args: Array<String>) {
val user = User(27, "huangjian")
//Kotlin 的解构特性。查看源码可以知道具体的实现逻辑
val (age, name, nickName) = user
println(age)
println(name)
println(nickName)
val map = mapOf<String, String>("key1" to "value1", "key2" to "value2")
//循环 map 是使用解构
for ((key, value) in map) {
println("$key ========= $value")
}
}
class User(val age: Int, val name: String) {
//operator 将一个函数标记为重载一个操作符或实现一个约定
//方法名称必须以 component+数字 命名
operator fun component1() = age
operator fun component2() = name
operator fun component3() = name
}
2.2 Kotlin 中的循环
代码如下:
KotlinMain.kt
package chapter2.class14
fun main(args: Array<String>) {
//[1-10]
for (i in 1..10) {
println(i)
}
//[1-10) 也就是 [1-9]
for (i in 1 until 10) {
println(i)
}
//[10-1] 逆序输出
for (i in 10 downTo 1) {
println(i)
}
//步长为 2
for (i in 1..10 step 2) {
println(i)
}
//[0-9]高级函数。传入闭包,表示循环的次数,it 表示当前循环的 index
repeat(10) { println(it)}
val arrayList = arrayListOf<String>("a", "b", "c", "d")
for (string in arrayList) {
println(string)
}
arrayList.forEach {
println(it)
}
//使用解构获取。map 可以直接使用,list 需要调用 withIndex 函数
for ((index, str) in arrayList.withIndex()) {
println("第$index 个元素是 $str")
}
repeat(arrayList.size) {
println("第 ${it + 1} 个元素是 ${arrayList.get(it)}")
}
}
2.3 Kotlin 中作用域函数
代码如下:
KotlinMain.kt 文件
package chapter2.class16
data class User(var age: Int, var name: String)
fun main(args: Array<String>) {
//作用域函数:Kotlin 内置的可对数据进行变换的函数,与集合的操作符类似。
// 但是作用域函数可以应用于所的对象
val user = User(27, "huangjian")
//let :返回闭包执行结果,let 可以有闭包参数
val letResult: String = user.let { user: User -> "let::${user.javaClass}" }
//当闭包参数只有一个时可以省略,用 it 来引用
// val letResult: String = user.let { "let::${it.javaClass}" }
println(letResult)
//run :返回闭包执行结果,run 不可以有闭包参数。
// 使用 this 来表示当前调用的对象 user ,不能使用 it 代替
val runResult: String = user.run { "run::${this.javaClass}" }
println(runResult)
//also :不返回闭包执行结果,also 可以有闭包参数。
user.also { println("also::${it.javaClass}") }
//apply :不返回闭包执行结果,apply 不可以有闭包参数
user.apply { println("apply::${this.javaClass}") }
// also 和 apply 返回值还是当前的对象,所以可以链式调用
user.also {
it.age = 18
}.apply {
println(this.toString())
}.apply {
this.name = "ADeveloperH"
}.also {
println(it.toString())
}
user.also {
it.name = ""
}
//takeIf 的闭包返回一个判断结果,为 false 时表示 takeIf 函数会返回空。
user.takeIf { it.name.length > 0 }?.also { println("姓名不为空") } ?: println("姓名为空")
//takeUnless 与 takeIf 刚好相反,闭包判断结果为 true 时函数会返回空
user.takeUnless { it.name.length > 0 }?.also { println("姓名为空") } ?: println("姓名不为空")
//循环执行当前闭包
repeat(5) {
println(it)
}
}
常用的操作符:
元素操作符:
contains:判断是否有指定元素
elementAt:返回对应的元素,越界会抛 IndexOutOfBoundsException
firstOrNull:返回符合条件的第一个元素,没有返回 Null
lastOfNull:返回符合条件的最后一个元素,没有返回 Null
indexOf:返回指定元素的下标,没有返回 -1
singleOrNull:返回符合条件的单个元素,如果没有符合或者超过一个,返回 Null
判断类:
any:判断集合中是否有满足条件的元素
all:判断集合中的元素是否都满足条件
none:判断集合中是否都不满足条件,是返回 true
count:查询集合中满足条件的元素个数
reduce:从第一项到最后一项进行累计
2.4 Kotlin 中自定义中缀表达式
代码如下:
KotlinMain.kt 文件
package chapter2.class17
fun main(args: Array<String>) {
//运算符重载:例如 .. 使用 operator 声明
// 中缀表达式:例如 step 使用 infix 声明
for (i in 1..100 step 2) {
}
println(5 VS 6)
println(7 VS 6)
//中缀表达式也可以这样通过正常的函数调用一样去调用
println(7.VS(7))
}
sealed class CompareResult {
object More : CompareResult() {
override fun toString(): String {
return "大于"
}
}
object Less : CompareResult() {
override fun toString(): String {
return "小于"
}
}
object Equal : CompareResult() {
override fun toString(): String {
return "等于"
}
}
}
//自定义中缀表达式。
// 扩展函数,Int 为函数的函数接收者
infix fun Int.VS(num: Int): CompareResult =
if (this < num) {
CompareResult.Less
} else if (this > num) {
CompareResult.More
} else {
CompareResult.Equal
}
2.5 Kotlin 中的特殊符号
代码如下:
KotlinMain.kt 文件
package chapter2.class18
fun main(args: Array<String>) {
`23`()
` `()
val string1 = "string"
val string2 = String("string".toByteArray())
//类似于 Java 中的 equals 方法。判断的是值
println(string1 == string2)
//类似于 Java 中的 == 。判断的是引用
println(string1 === string2)
//相当于直接使用 String
val typealiasString = TypealiasString("typealias".toByteArray())
println(typealiasString)
}
//使用反引号来设置函数名为数字(Kotlin 中是不能以数字为函数名的)
//Java 的语法不支持调用该函数。可以通过这种方式设置该函数只在 Kotlin 中使用
fun `23`() {
println("fun 数字函数名")
}
fun ` `() {
println("fun 空格函数名")
}
//相当于给 String 起别称。Kotlin 中有很多,例如 TypeAliases.kt 中相关的类
//用于跨平台的兼容性,可以实现映射
public typealias TypealiasString = java.lang.String
2.6 kotlin 中 val 和 var
代码如下:
package chapter2.class22
import java.util.*
fun main(args: Array<String>) {
val hello = Hello()
println(hello.string)
println(hello.string2)
var person = Person(1991)
println(person.age)
person.oneYearsLetter()
println(person.age)
println(s)
println(A.s)
println(B.s)
}
class Hello {
//var 变量可以有 get / set 方法
var string: String? = null
//get 和 set 方法不能直接使用 string(直接使用相当于调用 get 方法了),需要使用 field ,否则会出现循环引用堆栈溢出
get() {
return field + "hello"
}
set(value) {
field = value + "set"
}
//val 不可以有 set 方法,是不可变量不能再次赋值,但是可以通过重写 get 方法来改变它的值,但是 val 不是常量
val string2: String? = null
get() {
return field + "hello"
}
}
class Person(var birthYear: Int) {
val age: Int
get() {
return Calendar.getInstance().get(Calendar.YEAR) - birthYear
}
fun oneYearsLetter() {
birthYear--
}
}
//const 只能修辞 top-level 变量,或 object / companion object 中的属性。不能有 get / set 方法
//const 变量的值必须在编译期间确定下来,所以它的类型只能是 String 或基本类型
const val s = 0
object A {
const val s = 1
}
class B {
companion object {
const val s = 2
}
}