Kotlin的文件:.kt
基本数据类型
| 类型 | 位宽度 |
|---|---|
| Double | 64 |
| Float | 32 |
| Long | 64 |
| Int | 32 |
| Short | 16 |
| Byte | 8 |
定义常量:关键字 var
var <标识符> : <类型> = <初始化值>
var a : Int = 1
var b = 2
var c : Int
c = 3
定义变量:关键字 val
val <标识符> : <类型> = <初始化值>
函数定义:关键字fun,参数格式:参数:类型
//返回值类型是Int
fun sum(a: Int, b:Int) : Int{
return a + b
}
//自动推断返回值类型
fun sum(a : Int, b : Int) = a + b
//public 方法必须明确写出返回值类型
public fun sum(a : Int, b : Int) : Int = a + b
//无返回值类似void
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
可变长参数的函数,关键字vararg
fun vars(vararg v : Int){
for(vt in v){
print(vt)
}
}
fun main(args : Array<String>){
vars(1,2,3,4,5,) //输出12345
}
匿名函数lambda表达式
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
字符串模板
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值
var a = 1
// 模板中的简单名称:
val s1 = "a is $a" //a is 1
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a" //a was 1, but now is 2
NULL检查机制
//类型后面加?表示可为空, var age: String? = null 可以通过
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
以上情况 ,如果出现var age: String? = “null”,或var age: String? = " "
仍会报错
fun main(args: Array<String>) {
if (args.size < 2) {
print("Two integers expected")
return
}
//将字符串转换为Int
val x = parseInt(args[0])
val y = parseInt(args[1])
// 直接使用 `x * y` 会导致错误, 因为它们可能为 null.
if (x != null && y != null) {
// 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量
print(x * y)
}
}
| 符号 | 简单说明 |
|---|---|
| ? | 加在变量名后,系统在任何情况不会报它的空指针异常。 |
| !! | 加在变量名后,如果对象为null,那么系统一定会报异常! |
| ?: | 对象A ?: 对象B 表达式,意思为,当对象 A值为 null 时,那么它就会返回后面的对象 B |
| :: | 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法 |
| -> | 符号后边的值 是 前边表达式的返回值 |
类型检测及自动类型转换,使用 is 运算符
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
//在这里还有一种方法,与Java中instanceof不同,使用!is
// if (obj !is String){
// // XXX
// }
// 这里的obj仍然是Any类型的引用
return null
}
或者
fun getStringLength(obj: Any): Int? {
if (obj !is String)
return null
// 在这个分支中, `obj` 的类型会被自动转换为 `String`
return obj.length
}
或者
fun getStringLength(obj: Any): Int? {
// 在 `&&` 运算符的右侧, `obj` 的类型会被自动转换为 `String`
if (obj is String && obj.length > 0)
return obj.length
return null
}
区间
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
// 使用 until 函数排除结束元素
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
比较两个数字
Kotlin 中没有基础数据类型,只有封装的数字类型
三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小
fun main(args: Array<String>) {
val a: Int = 10000
val b: Int = 10000
println(a === b) // true,值相等,对象地址相等
//经过了装箱,创建了两个不同的对象
val boxedA: Int? = a
val anotherBoxedA: Int? = a
//虽然经过了装箱,但是值是相等的,都是10000
println(boxedA === anotherBoxedA) // false,值相等,对象地址不一样
println(boxedA == anotherBoxedA) // true,值相等
}
类型转换
val b: Byte = 1 // OK, 字面值是静态检测的
val a: Int = b // 错误
val c: Int = b.toInt() // OK
每种数据类型都有以下方法
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
位操作符
对于Int和Long类型,还有一系列的位操作符可以使用,分别是:
shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向
字符
Char 不能直接和数字操作,Char 必需是单引号 ’ 包含起来的。比如普通字符 ‘0’,‘a’。特殊字符可以用反斜杠转义
布尔
布尔用 Boolean 类型表示,它有两个值:true 和 false。
若需要可空引用布尔会被装箱。
内置的布尔运算有:
|| – 短路逻辑或
&& – 短路逻辑与
! - 逻辑非
数组
数组的创建两种方式:一种是使用函数arrayOf();另外一种是使用工厂函数
fun main(args: Array<String>) {
//[1,2,3]
val a = arrayOf(1, 2, 3)
//[0,2,4]
val b = Array(3, { i -> (i * 2) })
//读取数组内容
println(a[0]) // 输出结果:1
println(b[1]) // 输出结果:2
}
除了类Array,还有ByteArray, ShortArray, IntArray,用来表示各个类型的数组,省去了装箱操作
字符串
方括号 [] 语法可以很方便的获取字符串中的某个字符,也可以通过 for 循环来遍历
//遍历
for (c in str) {
println(c)
}
三个引号 “”" 扩起来的字符串,支持多行字符串
fun main(args: Array<String>) {
val text = """
多行字符串
多行字符串
"""
println(text) // 按照字符串样式输出,包括前置空格
}
可以通过 trimMargin() 方法来删除多余的空白。默认 | 用作边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")
fun main(args: Array<String>) {
val text = """
|多行字符串
|菜鸟教程
|多行字符串
|Runoob
""".trimMargin()
println(text) // 前置空格删除了
}
字符串模板
模板表达式以美元符($)开头,由一个简单的名字构成
fun main(args: Array<String>) {
val i = 10
val s = "i = $i" // 求值结果为 "i = 10"
println(s)
}
或者用花括号扩起来的任意表达式:
fun main(args: Array<String>) {
val s = "runoob"
val str = "$s.length is ${s.length}" // 求值结果为 "runoob.length is 6"
println(str)
}
原生字符串和转义字符串内部都支持模板。 如果你需要在原生字符串中表示字面值 $ 字符(它不支持反斜杠转义),可以用下列语法
fun main(args: Array<String>) {
val price = """
${'$'}9.99
"""
println(price) // 求值结果为 $9.99
}
IF 表达式
- 传统方式
- 将if表达式的结果赋值给一个变量
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
- 简单实现
val c = if (condition) a else b
使用区间
使用 in 运算符来检测某个数字是否在指定区间内,区间格式为 x…y :
fun main(args: Array<String>) {
val x = 5
if (x in 1..8) {
println("x 在区间内")
}
}
When 表达式
when 类似其他语言的 switch 操作符
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x 不是 1 ,也不是 2")
}
}
在 when 中,else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。
如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
也可以检测一个值在(in)或者不在(!in)一个区间或者集合中:
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
when 中使用 in 运算符来判断集合内是否包含某实例:
fun main(args: Array<String>) {
val items = setOf("apple", "banana", "kiwi")
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
}
For 循环
for 循环可以对任何提供迭代器(iterator)的对象进行遍历,语法如下:
/**
* collection是被遍历的对象
* item是遍历出来的每一个对象
*/
for (item in collection) print(item)
循环体可以是一个代码块:
for (item: Int in ints) {
// ……
}
通过索引遍历一个数组或者一个 list
for (i in array.indices) {
print(array[i])
}
用库函数 withIndex:
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
使用forEach :
//ints是要遍历的对象,it是遍历出来的每个对象
ints.forEach {
if (it == 0) return
print(it)
}
return返回
默认从最直接包围它的函数或者匿名函数返回
从最直接包围它的函数即 foo 中返回
fun foo() {
ints.forEach {
if (it == 0) return
print(it)
}
}
从 lambda 表达式中返回
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}
使用隐式标签
fun foo() {
ints.forEach {
if (it == 0) return@forEach
print(it)
}
}
类定义
Kotlin 类可以包含:构造函数和初始化代码块、函数、属性、内部类、对象声明。
class Demo{ // 类名为 Demo
// 大括号内是类体构成
}
可以像使用普通函数那样使用构造函数创建类实例:
val site = Demo() // Kotlin 中没有 new 关键字
// 使用 . 号来引用 属性
构造器
//User类有多个构造器
class User{
val username: String
var age: Int
constructor(username: String, age: Int){
this.username = username
this.age = age
}
constructor(username: String){
this.username = username
this.age = 10
}
}
//Person类只有一个构造器
class Person(username: String, age: Int){
private val username: String = username
private var age: Int = age
}
多个构造器要使用
constructor关键字
getter 和 setter
class Person {
var lastName: String = "zhang"
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
var heiht: Float = 145.4f
private set
}
// 测试
fun main(args: Array<String>) {
var person: Person = Person()
person.lastName = "wang"
println("lastName:${person.lastName}")
person.no = 9
println("no:${person.no}")
person.no = 20
println("no:${person.no}")
}
输出结果
lastName:WANG
no:9
no:-1
Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器
var age: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
在kotlin里面,如果在类型声明之后没有使用符号?,则表示该变量不会为null。但是这个时候会要求我们初始化一个值。有些时候,我们在声明变量的时候,并不能初始化这个变量
kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
主构造器
主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
}
抽象类
abstract class Bank {
abstract fun simpleInterest(p: Int, r: Double, t: Int) :Double
}
class SBI : Bank() {
override fun simpleInterest(p: Int, r: Double, t: Int): Double{
return (p*r*t)/100
}
}
class PNB : Bank() {
override fun simpleInterest(p: Int, r: Double, t: Int): Double{
return (p*r*t)/100
}
}
fun main(args: Array<String>) {
var sbi: Bank = SBI()
val sbiint = sbi.simpleInterest(1000,5.0,3)
println("SBI interest is $sbiint")
var pnb: Bank = PNB()
val pnbint = pnb.simpleInterest(1000,4.5,3)
println("PNB interest is $pnbint")
}
非抽象的open成员函数可以在抽象类中重载。
open class Car {
open fun run() {
println("Car is running..")
}
}
abstract class Honda : Car() {
override abstract fun run()
}
class City: Honda(){
override fun run() {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
println("Honda City is running..")
}
}
fun main(args: Array<String>){
val car = Car()
car.run()
val city = City()
city.run()
}
内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
类的修饰符
| classModifier | 类属性修饰符,标示类本身特性。 |
|---|---|
| abstract | 抽象类 |
| final | 类不可继承,默认属性 |
| enum | 枚举类 |
| open | 类可继承,类默认是final的 |
| annotation | 注解类 |
| accessModifier | 访问权限修饰符 |
|---|---|
| private | 仅在同一个文件中可见 |
| protected | 同一个文件中或子类可见 |
| public | 所有调用的地方都可见 |
| internal | 同一个模块中可见 |
如果一个类要被继承,可以使用 open 关键字进行修饰。
构造函数
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。
open class Person(var name : String, var age : Int){// 基类
}
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
}
// 测试
fun main(args: Array<String>) {
val s = Student("Runoob", 18, "S12346", 89)
println("学生名: ${s.name}")
println("年龄: ${s.age}")
println("学生号: ${s.no}")
println("成绩: ${s.score}")
}
如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法
/**用户基类**/
open class Person(name:String){
/**次级构造函数**/
constructor(name:String,age:Int):this(name){
//初始化
println("-------基类次级构造函数---------")
}
}
/**子类继承 Person 类**/
class Student:Person{
/**次级构造函数**/
constructor(name:String,age:Int,no:String,score:Int):super(name,age){
println("-------继承类次级构造函数---------")
println("学生名: ${name}")
println("年龄: ${age}")
println("学生号: ${no}")
println("成绩: ${score}")
}
}
fun main(args: Array<String>) {
var s = Student("Runoob", 18, "S12345", 89)
}
重写
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词
如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。
open class A {
open fun f () { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } //接口的成员变量默认是 open 的
fun b() { print("b") }
}
class C() : A() , B{
override fun f() {
super<A>.f()//调用 A.f()
super<B>.f()//调用 B.f()
}
}
fun main(args: Array<String>) {
val c = C()
c.f();
}
Kotlin 扩展
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
扩展函数
扩展函数可以在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式:
fun receiverType.functionName(params){
body
}
- receiverType:表示函数的接收者,被扩展的对象
- functionName:扩展函数的名称
- params:扩展函数的参数,可以为NULL
// 扩展函数 swap,调换不同位置的值
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this 对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
fun main(args: Array<String>) {
val l = mutableListOf(1, 2, 3)
// 位置 0 和 2 的值做了互换
l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值
println(l.toString()) //[3, 2, 1]
}
扩展一个空对象
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。例如
fun Any?.toString(): String {
if (this == null) return "null"
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
// 解析为 Any 类的成员函数
return toString()
}
fun main(arg:Array<String>){
var t = null
println(t.toString())
}
伴生对象的扩展
如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数和属性。
伴生对象通过"类名."形式调用伴生对象,伴生对象声明的扩展函数,通过用类名限定符来调用
class MyClass {
companion object { } // 将被称为 "Companion"
}
fun MyClass.Companion.foo() {
println("伴随对象的扩展函数")
}
val MyClass.Companion.no: Int
get() = 10
fun main(args: Array<String>) {
println("no:${MyClass.no}")
MyClass.foo()
}
数据类
Kotlin 可以创建一个只包含数据的类,关键字为 data:
data class User(val name: String, val age: Int)
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
- equals() / hashCode()
- toString() 格式如 “User(name=John, age=42)”
- componentN() functions 对应于属性,按声明顺序排列
- copy() 函数
如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:
-
主构造函数至少包含一个参数。
-
所有的主构造函数的参数必须标识为val 或者 var ;
-
数据类不可以声明为 abstract, open, sealed 或者 inner;
-
数据类不能继承其他类 (但是可以实现接口)。
复制使用 copy() 函数,我们可以使用该函数复制对象并修改部分属性
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
val jack = User(name = "Jack", age = 1)
val newJack = jack.copy(age = 2)
println(jack)
println(newJack )
}
1346

被折叠的 条评论
为什么被折叠?



