在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):heimashi/kotlin_tips开源软件地址(OpenSource Url):https://github.com/heimashi/kotlin_tips开源编程语言(OpenSource Language):Kotlin 79.8%开源软件介绍(OpenSource Introduction):怎么用Kotlin去提高生产力:Kotlin Tips汇总Kotlin相对于Java的优势,以及怎么用Kotlin去简洁、务实、高效、安全的开发,每个tip都有详细的说明和案例代码,争取把每个tip分析得清楚易懂,会不断的更新维护tips,欢迎fork进来加入我们一起来维护,有问题的话欢迎提Issues。
目录
Tip1-更简洁的字符串三个引号详见案例代码KotlinTip1 Kotlin中的字符串基本Java中的类似,有一点区别是加入了三个引号"""来方便长篇字符的编写。 而在Java中,这些都需要转义,先看看java中的式例 public void testString1() {
String str1 = "abc";
String str2 = "line1\n" +
"line2\n" +
"line3";
String js = "function myFunction()\n" +
"{\n" +
" document.getElementById(\"demo\").innerHTML=\"My First JavaScript Function\";\n" +
"}";
System.out.println(str1);
System.out.println(str2);
System.out.println(js);
} kotlin除了有单个双引号的字符串,还对字符串的加强,引入了三个引号,"""中可以包含换行、反斜杠等等特殊字符: /*
* kotlin对字符串的加强,三个引号"""中可以包含换行、反斜杠等等特殊字符
* */
fun testString() {
val str1 = "abc"
val str2 = """line1\n
line2
line3
"""
val js = """
function myFunction()
{
document.getElementById("demo").innerHTML="My First JavaScript Function";
}
""".trimIndent()
println(str1)
println(str2)
println(js)
} 字符串模版同时,Kotlin中引入了字符串模版,方便字符串的拼接,可以用$符号拼接变量和表达式 /*
* kotlin字符串模版,可以用$符号拼接变量和表达式
* */
fun testString2() {
val strings = arrayListOf("abc", "efd", "gfg")
println("First content is $strings")
println("First content is ${strings[0]}")
println("First content is ${if (strings.size > 0) strings[0] else "null"}")
} 值得注意的是,在Kotlin中,美元符号 /*
* Kotlin中,美元符号$是特殊字符,在字符串中不能直接显示,必须经过转义,方法1是用反斜杠,方法二是${'$'}
* */
fun testString3() {
println("First content is \$strings")
println("First content is ${'$'}strings")
} Tip2-Kotlin中大多数控制结构都是表达式首先,需要弄清楚一个概念语句和表达式,然后会介绍控制结构表达式的优点:简洁 语句和表达式是什么?
Kotlin与Java的区别
详见案例代码tip2 Example1:if语句java中,if 是语句,没有值,必须显式的return /*
* java中的if语句
* */
public int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
} kotlin中,if 是表达式,不是语句,因为表达式有值,可以作为值return出去 /*
* kotlin中,if 是表达式,不是语句,类似于java中的三目运算符a > b ? a : b
* */
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
} 上面的if中的分支最后一行语句就是该分支的值,会作为函数的返回值。这其实跟java中的三元运算符类似, /*
* java的三元运算符
* */
public int max2(int a, int b) {
return a > b ? a : b;
} 上面是java中的三元运算符,kotlin中if是表达式有值,完全可以替代,故kotlin中已没有三元运算符了,用if来替代。 上面的max函数还可以简化成下面的形式 /*
* kotlin简化版本
* */
fun max2(a: Int, b: Int) = if (a > b) a else b Example2:when语句Kotlin中的when非常强大,完全可以取代Java中的switch和if/else,同时,when也是表达式,when的每个分支的最后一行为当前分支的值 先看一下java中的switch /*
* java中的switch
* */
public String getPoint(char grade) {
switch (grade) {
case 'A':
return "GOOD";
case 'B':
case 'C':
return "OK";
case 'D':
return "BAD";
default:
return "UN_KNOW";
}
} java中的switch有太多限制,我们再看看Kotlin怎样去简化的 /*
* kotlin中,when是表达式,可以取代Java 中的switch,when的每个分支的最后一行为当前分支的值
* */
fun getPoint(grade: Char) = when (grade) {
'A' -> "GOOD"
'B', 'C' -> {
println("test when")
"OK"
}
'D' -> "BAD"
else -> "UN_KNOW"
} 同样的,when语句还可以取代java中的if/else if,其是表达式有值,并且更佳简洁 /*
* java中的if else
* */
public String getPoint2(Integer point) {
if (point > 100) {
return "GOOD";
} else if (point > 60) {
return "OK";
} else if (point.hashCode() == 0x100) {
//...
return "STH";
} else {
return "UN_KNOW";
}
} 再看看kotlin的版本,使用不带参数的when,只需要6行代码 /*
* kotlin中,when是表达式,可以取代java的if/else,when的每个分支的最后一行为当前分支的值
* */
fun getPoint2(grade: Int) = when {
grade > 90 -> "GOOD"
grade > 60 -> "OK"
grade.hashCode() == 0x100 -> "STH"
else -> "UN_KNOW"
} Tip3-更好调用的函数-显式参数名及默认参数值显式参数名Kotlin的函数更加好调用,主要是表现在两个方面:1,显式的标示参数名,可以方便代码阅读;2,函数可以有默认参数值,可以大大减少Java中的函数重载。 例如现在需要实现一个工具函数,打印列表的内容: 详见案例代码KotlinTip3 /*
* 打印列表的内容
* */
fun <T> joinToString(collection: Collection<T>,
separator: String,
prefix: String,
postfix: String): String {
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
/*
* 测试
* */
fun printList() {
val list = listOf(2, 4, 0)
// 不标明参数名
println(joinToString(list, " - ", "[", "]"))
// 显式的标明参数名称
println(joinToString(list, separator = " - ", prefix = "[", postfix = "]"))
} 如上面的代码所示,函数joinToString想要打印列表的内容,需要传入四个参数:列表、分隔符、前缀和后缀。 由于参数很多,在后续使用该函数的时候不是很直观的知道每个参数是干什么用的,这时候可以显式的标明参数名称,增加代码可读性。 默认参数值同时,定义函数的时候还可以给函数默认的参数,如下所示: /*
* 打印列表的内容,带有默认的参数,可以避免java的函数重载
* */
fun <T> joinToString2(collection: Collection<T>,
separator: String = ", ",
prefix: String = "",
postfix: String = ""): String {
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
/*
* 测试
* */
fun printList3() {
val list = listOf(2, 4, 0)
println(joinToString2(list, " - "))
println(joinToString2(list, " , ", "["))
} 这样有了默认参数后,在使用函数时,如果不传入该参数,默认会使用默认的值,这样可以避免Java中大量的函数重载。 @JvmOverloads在java与kotlin的混合项目中,会发现用kotlin实现的带默认参数的函数,在java中去调用的化就不能利用这个特性了,还是需要给所有参数赋值,像下面java这样: List<Integer> arr = new ArrayList<Integer>() {{add(2);add(4);add(0);}};
String res = joinToString2(arr, "-", "", "");
System.out.println(res); 这时候可以在kotlin的函数前添加注解@JvmOverloads,添加注解后翻译为class的时候kotlin会帮你去生成多个函数实现函数重载,kotlin代码如下: /*
* 通过注解@JvmOverloads解决java调用kotlin时不支持默认参数的问题
* */
@JvmOverloads
fun <T> joinToString2New(collection: Collection<T>,
separator: String = ", ",
prefix: String = "",
postfix: String = ""): String {
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
} 这样以后,java调用kotlin的带默认参数的函数就跟kotlin一样方便了: List<Integer> arr = new ArrayList<Integer>() {{add(2);add(4);add(0);}};
String res = joinToString2New(arr, "-");
System.out.println(res);
String res2 = joinToString2New(arr, "-", "=>");
System.out.println(res2); Tip4-扩展函数和属性扩展函数和扩展属性是Kotlin非常方便实用的一个功能,它可以让我们随意的扩展第三方的库,你如果觉得别人给的SDK的Api不好用,或者不能满足你的需求,这时候你可以用扩展函数完全去自定义。 扩展函数例如String类中,我们想获取最后一个字符,String中没有这样的直接函数,你可以用.后声明这样一个扩展函数: 详见案例代码KotlinTip4 /*
* 扩展函数
* */
fun String.lastChar(): Char = this.get(this.length - 1)
/*
* 测试
* */
fun testFunExtension() {
val str = "test extension fun";
println(str.lastChar())
} 这样定义好lastChar()函数后,之后只需要import进来后,就可以用String类直接调用该函数了,跟调用它自己的方法没有区别。这样可以避免重复代码和一些静态工具类,而且代码更加简洁明了。 例如我们可以改造上面tip3中的打印列表内容的函数: /*
* 用扩展函数改造Tip3中的列表打印内容函数
* */
fun <T> Collection<T>.joinToString3(separator: String = ", ",
prefix: String = "",
postfix: String = ""): String {
val result = StringBuilder(prefix)
for ((index, element) in withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
fun printList4() {
val list = listOf(2, 4, 0)
println(list.joinToString3("/"))
} 扩展属性除了扩展函数,还可以扩展属性,例如我想实现String和StringBuilder通过属性去直接获得最后字符: /*
* 扩展属性 lastChar获取String的最后一个字符
* */
val String.lastChar: Char
get() = get(length - 1)
/*
* 扩展属性 lastChar获取StringBuilder的最后一个字符
* */
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
setCharAt(length - 1, value)
}
/*
* 测试
* */
fun testExtension() {
val s = "abc"
println(s.lastChar)
val sb = StringBuilder("abc")
println(sb.lastChar)
} 定义好扩展属性后,之后只需import完了就跟使用自己的属性一样方便了。 Why?Kotlin为什么能实现扩展函数和属性这样的特性?在Kotlin中要理解一些语法,只要认识到Kotlin语言最后需要编译为class字节码,Java也是编译为class执行,也就是可以大致理解为Kotlin需要转成Java一样的语法结构, Kotlin就是一种强大的语法糖而已,Java不具备的功能Kotlin也不能越界的。
我们看看将上面的扩展函数转成Java后的代码 /*
* 扩展函数会转化为一个静态的函数,同时这个静态函数的第一个参数就是该类的实例对象
* */
public static final char lastChar(@NotNull String $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.charAt($receiver.length() - 1);
}
/*
* 获取的扩展属性会转化为一个静态的get函数,同时这个静态函数的第一个参数就是该类的实例对象
* */
public static final char getLastChar(@NotNull StringBuilder $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.charAt($receiver.length() - 1);
}
/*
* 设置的扩展属性会转化为一个静态的set函数,同时这个静态函数的第一个参数就是该类的实例对象
* */
public static final void setLastChar(@NotNull StringBuilder $receiver, char value) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
$receiver.setCharAt($receiver.length() - 1, value);
} 查看上面的代码可知:对于扩展函数,转化为Java的时候其实就是一个静态的函数,同时这个静态函数的第一个参数就是该类的实例对象,这样把类的实例传入函数以后,函数内部就可以访问到类的公有方法。 对于扩展属性也类似,获取的扩展属性会转化为一个静态的get函数,同时这个静态函数的第一个参数就是该类的实例对象,设置的扩展属性会转化为一个静态的set函数,同时这个静态函数的第一个参数就是该类的实例对象。 函数内部可以访问公有的方法和属性。顶层的扩展函数是static的,不能被override
|
请发表评论