博客主页java
对比下Java的类Person与Kotlin的类Person区别:git
// java public class Person { private final String name; public Person(String name) { this.name = name; } // 提供一个getter访问器 public String getName() { return name; } } // kotlin // 在Kotlin中,public是默认的可见性,能够省略 class Person(val name: String)
在Kotlin中,在类中声明一个属性和声明一个变量同样,使用 val(只读的) 和 var(可变的) 关键字。正则表达式
若是属性的名称以 is 开头,getter不会增长任何的前缀,而它的setter名称中的is会被替换成set。segmentfault
class Person( // 只读属性,生成一个字段和一个简单的getter val name: String, // 可写属性,一个字段,一个getter和一个setter var address: String, var isMarried: Boolean ) // kotlin转换为java public final class Person { @NotNull private final String name; @NotNull private String address; private boolean isMarried; @NotNull public final String getName() { return this.name; } @NotNull public final String getAddress() { return this.address; } public final boolean isMarried() { return this.isMarried; } public final void setMarried(boolean var1) { this.isMarried = var1; } public final void setAddress(@NotNull String var1) { this.address = var1; } public Person(@NotNull String name, @NotNull String address, boolean isMarried) { this.name = name; this.address = address; this.isMarried = isMarried; } }
在java 和 kotlin 中调用Person类区别:数组
// java中使用Person Person person = new Person("kerwin", "anqing", false); person.setMarried(true); System.out.println(person.getName() + " : " + person.getAddress() + " : " + person.isMarried()); // kerwin : anqing : true // kotlin中使用Person val person = Person("kerwin", "anqing", false) person.isMarried = true println("${person.name} : ${person.address} : ${person.isMarried}") // kerwin : anqing : true
下面自定义一个属性isSquare,实现getter,它的值是每次访问属性的时计算出来的。app
// kotlin class Rectangle( val height: Int, val width: Int ) { // 自定义访问器 val isSquare: Boolean // 声明属性的getter // 也能够这样写: get() = width == height get() { return width == height } } // kotlin转java public final class Rectangle { private final int height; private final int width; public final boolean isSquare() { return this.width == this.height; } public final int getHeight() { return this.height; } public final int getWidth() { return this.width; } public Rectangle(int height, int width) { this.height = height; this.width = width; } }
每个kotlin文件都能以一条package语句开头,而文件中定义的全部的声明(类、函数、属性)都会被放到这个包中。若是包不相同,则须要导入它们,使用关键字import函数
kotlin不区分导入的是类仍是函数,且容许使用import关键字导入任何种类的声明。可直接导入顶层函数的名称工具
// 包声明 package com.kerwin.kotlin.demo class Rectangle( val height: Int, val width: Int ) { val isSquare: Boolean get() = width == height } // 在com.kerwin.kotlin.demo包中定义函数 fun createRectangle(): Rectangle { return Rectangle(12, 23) }
导入其余包中的函数布局
// 包声明 package com.kerwin.kotlin.demo1 // 导入函数名称 import com.kerwin.kotlin.demo.createRectangle fun main(args: Array<String>) { println(createRectangle().isSquare) }
在kotlin中,能够把多个类放在同一个文件中,文件的名字还能够随意选择。kotlin也没有对磁盘上源文件的布局强加任何限制。包层级结构不须要遵循目录层级结构。post
kotlin声明一个枚举类,使用 enum class 关键字。枚举类中定义任何方法,就要使用分号(;)把枚举常量列表和方法定义分开。
// 声明一个带有属性的枚举类 enum class Color( // 声明枚举常量的属性 val r: Int, val g: Int, val b: Int ) { // 在每一个常量建立时指定属性值 RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255); // 必需要有分号 // 给枚举类定义一个方法 fun rgb() = (r * 256 + g) * 256 + b }
when 是一个有返回值的表达式。
// kotlin中不须要在每一个分支都写上break语句。若是匹配成功。只有对应的分支会执行 fun getColorString(color: Color) = when(color) { Color.RED -> "red" Color.BLUE -> "blue" Color.ORANGE -> "orange" Color.YELLOW -> "yellow" Color.GREEN -> "green" }
能够把多个值合并到同一个分支,只须要用逗号(,)隔开这些值就能够
fun getWarmth(color: Color) = when(color) { Color.RED, Color.ORANGE, Color.YELLOW -> "warm" Color.BLUE, Color.GREEN -> "cold" }
when 容许使用任何对象做为分支条件。
// 在when分支中使用不一样的对象 fun mix(c1: Color, c2: Color) = // when 表达式的实参能够是任何对象,它被检查是否与分支条件相等 when (setOf(c1, c2)) { setOf(Color.RED, Color.YELLOW) -> Color.ORANGE setOf(Color.YELLOW, Color.BLUE) -> Color.GREEN else -> throw Exception("dirty color") }
若是没有给when表达式提供参数,分支条件就是任意的布尔表达式.
fun mixOptimized(c1: Color, c2: Color) = when { (c1 == Color.RED && c2 == Color.YELLOW) || (c1 == Color.YELLOW && c2 == Color.RED) -> Color.ORANGE (c1 == Color.YELLOW && c2 == Color.BLUE) || (c1 == Color.BLUE && c2 == Color.YELLOW) -> Color.GREEN else -> throw Exception("dirty color") }
定义一个函数:(1 + 2) + 4 算术表达式求值。智能转换只在变量通过is检查后再也不发生变化的状况下有效。属性必须是一个val属性,且不能有自定义的访问器。使用as关键字来表示到特定类型的显示转换。
// 仅做为一个标记接口 interface Expr // 实现接口使用冒号(:),后面跟上接口名称 class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Expr // 使用if层叠对表达式求值 fun eval(e: Expr): Int { // 使用is检查来判断一个变量是不是某种类型 if (e is Num) { // 显示的转换成类型Num是多余的 val num = e as Num return num.value } if (e is Sum) { // 变量e智能转换了类型 return eval(e.left) + eval(e.right) } throw IllegalArgumentException("Unknown") } >>> println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
kotlin没有三元运算符,由于if表达式有返回值。若是if分支只有一个表达式,花括号能够省略。若是if分支是一个代码块,代码块中的最后一个表达式会被做为结果返回。
// 使用有返回值的if表达式 fun eval(e: Expr): Int { if (e is Num) { e.value } else if (e is Sum) { eval(e.left) + eval(e.right) } else { throw IllegalArgumentException("Unknown") } }
可使用when代替if层叠
// when容许检查实参值的类型 fun eval(e: Expr): Int = when (e) { is Num -> e.value is Sum -> eval(e.left) + eval(e.right) else -> throw IllegalArgumentException("Unknown") }
if 和 when 均可以使用代码块做为分支体,那么代码块中的最后一个表达式就是结果。
fun evalWithLogging(e: Expr): Int = when (e) { is Num -> { println("num: ${e.value}") e.value } is Sum -> { val left = evalWithLogging(e.left) val right = evalWithLogging(e.right) println("sum: $left + $right") left + right } else -> throw IllegalArgumentException("Unknown") }
和java没有区别,有while循环 和 do-while循环
区间:两个值之间的间隔,这两个值一般是数字,一个起始值,一个结束值。使用 .. 运算符表示区间。
kotlin区间是包含的或者闭合的。是包含结束值的。若是不包含结束值,使用 until 函数
val oneToTen = 1..10 // 不包含size for (x in 0 until size) 等价于 for (x in 0..size - 1)
迭代1到100之间的全部数字
fun fizzBuzz(i: Int) = when { i % 15 == 0 -> "FizzBuzz " i % 3 == 0 -> "Fizz " i % 5 == 0 -> "Buzz " else -> "$i " } >>> for (i in 1..100) { print(fizzBuzz(i)) }
迭代带步长的100到1的区间
// 100 downTo 1 是递减的数列(步长为-1) // step 2 把步长的绝对值变成2,可是方向保持不变(步长被设置成了为-2) for (i in 100 downTo 1 step 2) { print(fizzBuzz(i)) }
初始化map并迭代。..语法能够建立数字区间,也能够建立字符区间。
// 初始化map,使用TreeMap让键排序 val binaryMap = TreeMap<Char, String>() // 使用字符区间迭代从A到F之间的字符 for (c in 'A'..'F') { // 字符二进制 val binary = Integer.toBinaryString(c.toInt()) // 简明语法:map[key] = value 设置值;map[key] 读取值 // 这行等价于:binaryMap.put(c, binary) binaryMap[c] = binary } // 迭代map,把键和值赋给两个变量 for ((letter, binary) in binaryMap) { println("$letter : $binary") }
能够在迭代集合时,使用当前下标
val list = arrayListOf("10", "11", "12") // 迭代集合时使用下标 for ((index, element) in list.withIndex()) { println("$index : $element") }
关键字 in 能够迭代区间或者集合,还能够用来检查区间或者集合是否包含了某个值。!in 检查一个值是否不在区间中。
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z' // !in 检查是否不在区间中 fun isNotDigit(c: Char) = c !in '0'..'9'
in 运算符 和 !in 检查能够做为 when分支
fun recognize(c: Char) = when (c) { // 检查值是否在0到9的区间以内 in '0'..'9' -> "It's a digit." in 'a'..'z', in 'A'..'Z' -> "It's a letter." else -> "I don't know." }
// 建立set val set = hashSetOf(1, 3, 5) // kotlin的javaClass等价于java的getClass println(set.javaClass) // class java.util.HashSet // 建立list val list = arrayListOf(1, 3, 5) println(list.javaClass) // class java.util.ArrayList // 建立map // to 并非一个特殊的结构,而是一个普通函数 val map = hashMapOf(1 to "one", 3 to "three", 5 to "five") println(map.javaClass) // class java.util.HashMap
kotlin中能够经过下面方式获取一个列表中最后一个元素,或者获得一个数字列表的最大值
val list = listOf("abc", "def", "ker") println(list.last()) // ker val set = setOf(1, 23, 4) println(set.max()) // 23
java集合都有一个默认的toString方法实现。假设不想使用默认的实现,须要本身实现或者使用第三方库,如:guava 和Apache Commons。
// 本身实现joinToString函数 fun <T> joinToString( collection: Collection<T>, prefix: String, postfix: String, separator: String ): String { val result = StringBuilder(prefix) for ((index, element) in collection.withIndex()) { if (index > 0) result.append(separator).append(" ") result.append(element) } result.append(postfix) return result.toString() } >>> val list = listOf("abc", "def", "ker") >>> println(joinToString(list, "(", ")", ";")) (abc; def; ker)
// 这种方式不能知道每一个参数对应什么含义 joinToString(list, "(", ")", ";") // kotlin,能够显式的标明一些参数名称 // 若是在调用一个函数时,指明了一个参数名称,为了不混淆,那它以后全部参数都须要标明名称 joinToString(list, prefix = "(", postfix = ")", separator = ";")
java中存在一个广泛的问题,一些类的重载函数不少,如:Thread类(8个构造方法)。
在kotlin中,能够在声明函数的时候,指定参数的默认值,就能够避免建立重载的函数。
fun <T> joinToString( collection: Collection<T>, prefix: String = "", // 有默认的参数 postfix: String = "", separator: String = ", " ): String // 调用的时候,能够省略只有排在末尾的参数 >>> joinToString(list) >>> joinToString(list, "(", ")") // 若是使用命名参数,能够省略中间的一些参数 >>> joinToString(list, separator = "; ") // 也能够以任意顺序只给定须要的参数 >>> joinToString(list, separator = "; ", prefix = "[")
在kotlin中,不须要建立静态工具类,能够把函数直接放到代码文件的顶层,不用从属任何的类。
声明joinToString做为顶层函数
// join.kt package strings fun joinToString(...): String { ... } // 当编译join.kt这个文件时,会生成一些类,JoinKt.java。由于JVM只能执行类中的代码。 // 且join.kt文件中的全部顶层函数编译为JoinKt.java这个类的静态函数 // 从java中调用这些函数:JoinKt.joinToString(list, "", "", ""); public final class JoinKt { @NotNull public static final String joinToString(@NotNull Collection collection, @NotNull String prefix, @NotNull String postfix, @NotNull String separator) { ... } }
修改文件类名:
要修改包含kotlin顶层函数的生成的类的名称,须要为这个文件添加 @file:JvmName 注解,将其放到这个文件的开头,位于包名的前面
// 注解指定类名 @file:JvmName("StringUtils") // 包的声明跟在文件注解后 package strings fun <T> joinToString( collection: Collection<T>, prefix: String = "", postfix: String = "", separator: String = ", " ): String { ... } // 编译后,生成StringUtils.class文件 // 在java代码中调用这个函数:StringUtils.joinToString(list, "", "", "")
顶层属性和函数同样,属性也能够放到文件的顶层。
package strings val LINE_SEPARATOR = "\n" // 编译后,私有的静态常量。 private static final String LINE_SEPARATOR = "\n"; // 使用const修饰,const val LINE_SEPARATOR = "\n" 编译后生成public的静态常量 public static final String LINE_SEPARATOR = "\n";
扩展函数就是把要扩展的类或者接口的名称,放到即将添加的函数前面。这个类的名称称为接受者类型,用来调用这个扩展函数的那个对象,叫做接受者对象。可是扩展函数不能访问私有的或者受保护的成员。
package strings // 为String类添加本身的方法:字符串的最后一个字符 // String:接受者类型 this:接受者对象 fun String.lastChar(): Char = this[this.length - 1] >>> println("abc".lastChar())
kotlin容许用和导入类同样的语法来导入单个函数。
import strings.lastChar // 使用 * 来导入也能够: import strings.* // 还可使用关键字 as 来修改导入的类或者函数名称 // import strings.lastChar as last // 调用: val lastChar = "abc".last() val lastChar = "abc".lastChar()
扩展函数就是静态函数,它把调用对象做为了它的第一个参数。
// 把接受者对象做为第一个参数传进去便可 StringUtils.lastChar("abc")
为元素的集合类Collection添加一个扩展函数,而后给全部的参数添加一个默认值。
@file:JvmName("StringUtils") package strings import java.lang.StringBuilder // Collection<T>: 接受者类型 // 为Collection<T> 声明一个扩展函数 fun <T> Collection<T>.joinToString( prefix: String = "", postfix: String = "", separator: String = ", " ): String { val result = StringBuilder(prefix) // this: 接受者对象 for ((index, element) in this.withIndex()) { if (index > 0) result.append(separator).append(" ") result.append(element) } result.append(postfix) return result.toString() } >>> val list = listOf("abc", "def", "ker") >>> println(list.joinToString(prefix = "{", postfix = "}", separator = "; ")) {abc; def; ker}
kotlin中不能重写扩展函数,由于kotlin会把它们看成静态函数对待。
扩展属性必须定义getter函数,由于没有支持字段,所以没有默认getter实现,初始化也不能够,由于没有地方存储初始值。
// 声明一个扩展属性 val String.lastChar: Char get() = this[this.length - 1]
也能够声明一个可变的扩展属性
var StringBuilder.lastChar: Char // getter属性 get() = this.get(this.length - 1) // setter属性 set(value) { this.setCharAt(length - 1, value) } >>> val sb = StringBuilder("ab?") >>> sb.lastChar = '!' >>> println(sb) // ab! // 若是从java中访问扩展属性,显式的调用它的getter函数 StringUtils.getLastChar(new StringBuilder("abc"));
val list = listOf("abc", "def", "ker") // last函数被定义为List的扩展函数 println(list.last()) // ker // public fun <T> List<T>.last(): T { ... }
kotlin中,在参数上使用vararg修饰符;java中使用三个点(...)
val list = listOf("abc", "def", "ker") // listOf函数在库中声明 public fun <T> listOf(vararg elements: T): List<T>
还有一个区别:当须要传递的参数是已经包装在数组中时,在java中,能够按原样传递数组;kotlin中要求显式的解包数组,以便每一个数组元素在函数中能做为单独的参数来调用。
val array = arrayOf(11, 12, 13) // 使用展开运算符(*)传递数组 val list = listOf("10", *array) >>> println(list) // [10, 11, 12, 13]
val map = mapOf(1 to "one", 2 to "two") // mapOf函数在库中的声明 public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> // 通常函数调用: 2.to("two"); 中缀符号调用to函数:2 to "two" // to 不是内置结构,而是一种特殊的函数调用,称为中缀调用 // to 函数在库中的声明 // 中缀调用能够与只有一个参数的函数一块儿使用,使用中缀符号调用函数,须要使用**infix**修饰符标记。 public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
能够直接用Pair的内容来初始化两个变量,称为解构声明。解构声明还可使用map的key和value内容来初始化两个变量。也适用于循环,如: withIndex
val (number, name) = 1 to "one" val list = listOf("abc", "def", "ker") for ((index, element) in list.withIndex()) { println("$index : $element") }
kotlin能够在字符串字面值中引用局部变量,只须要在变量名称前面加上字符$
val x = 12 println("x = $x") // x = 12 // 若是对它转义,不会把x解析成变量的引用 println("x = \$x") //x = $x // 若是引用复杂的表达式,须要把表达式用花括号括起来 val x= 1 val y = 2 println("x + y = ${x + y}")
// 在java中,指望获得[12, 345-6, A],可是返回是一个空数组,由于点号(.)表示任何字符的正则表达式 System.out.println(Arrays.toString("12.345-6.A".split("."))); // [] // 在kotlin中,能够显示的建立一个正则表达式分割字符串 // 须要转义表示字面量,而不是通配符 // 使用扩展函数toRegex将字符串转换为正则表达式 println("12.345-6.A".split("\\.".toRegex())) // [12, 345-6, A] // kotlin中的split扩展函数的其余重载支持任意数量的纯文本字符串分隔符; // 指定多个分隔符 println("12.345-6.A".split(".", "-")) // [12, 345, 6, A]
使用String的扩展函数来解析文件路径。
fun parsePath(path: String) { val directory = path.substringBeforeLast("/") val fullName = path.substringAfterLast("/") val fileName = fullName.substringBeforeLast(".") val extension = fullName.substringAfterLast(".") println("Dir: $directory, fileName: $fileName, ext: $extension") //Dir: /User/kerwin/book, fileName: readme, ext: md } >>> parsePath("/User/kerwin/book/readme.md")
使用正则表达式解析文件路径
fun parsePath(path: String) { // 正则表达式写在一个 三重引号的字符串中,不须要对任何字符进行转义,包括反斜线,因此能够用\.而不是\\.表示点 // (.+) 目录,/ 最后一个斜线,(.+) 文件名,\. 最后一个点,(.+) 扩展名 val regex = """(.+)/(.+)\.(.+)""".toRegex() val matchResult = regex.matchEntire(path) if (matchResult != null) { val (directory, fileName, extension) = matchResult.destructured println("Dir: $directory, fileName: $fileName, ext: $extension") } }
能够避免转义字符,且能够包含任何字符,包括换行符,用于格式化代码的缩进。
val string = """| // .|// .|/ \ """ // 能够去掉缩进 // 先向字符串内容添加前缀,标记边距的结尾,而后调用trimMargin来删除每行中的前缀和前面的空格 >>> println(string.trimMargin(".")) | // |// |/ \
带重复代码的函数
fun saveUser(user: User) { if (user.name.isEmpty()) { throw IllegalArgumentException("Can't save user ${user.id}, name is empty.") } if (user.address.isEmpty()) { throw IllegalArgumentException("Can't save user ${user.id}, address is empty.") } // save user ... }
提取局部函数来避免重复,在布局函数中能够访问外层函数的参数
fun saveUser(user: User) { fun validate(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("Can't save user ${user.id}, $fieldName is empty.") } } validate(user.name, "name") validate(user.address, "address") // save user ... }
提取逻辑到扩展函数
class User(val id: Int, val name: String, val address: String) fun User.validateBeforeSave(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("Can't save user ${this.id}, $fieldName is empty.") } } fun saveUser(user: User) { user.validateBeforeSave(user.name, "name") user.validateBeforeSave(user.address, "address") // save user ... }
若是个人文章对您有帮助,不妨点个赞鼓励一下(^_^)