1.基础语法(引用部分代码来自菜鸟教程)
主函数:和java不一样的是没有被类包裹,看到了熟悉的println函数。
kotlin中有一个少见的就是变量写法:变量名 :类型(第一个字母大写)
不用写分号
fun main(args: Array<String>) { // 包级可见的函数,接受一个字符串数组作为参数
println("Hello World!") // 分号可以省略
}
关键字列表
- fun :函数声明
- vararg:
- field:后端变量
- super
函数:
函数定义:fun是关键字,fun sum(a:Int,b:Int):Int{} 最后的:Int表示函数最终返回整形数据
fun sum(a: Int, b: Int): Int { // Int 参数,返回值 Int
return a + b
}
表达式也可以作为函数定义:这个时候,返回类型自动判断,public声明的要明确类型
fun sum(a: Int, b: Int) = a + b
public fun sum(a: Int, b: Int): Int = a + b // public 方法则必须明确写出返回类型
无返回值函数:Unit,可以省略,Unit与c++的void一样
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
可变参数长度的函数:vararg:声明v是动态的整形数据,感觉像php与js的弱类型样子。
fun vars(vararg v:Int){
for(item in v){
print(item)
}
}
// 测试
fun main(args: Array<String>) {
vars(1,2,3,4,5) // 输出12345
}
匿名函数:
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
常量与变量
这kotlin分为可变和固定的类似于JS的const变量:关键字 val是不可变常量 var 是变量;Kotlin这真是集百家之大长啊,JS,C++,C,PHP,(JAVA不会,很尴尬啊,听说kt和它很接近)都有影子。。。
val <标识符> : <类型> = <初始化值>
var <标识符> : <类型> = <初始化值>
val a: Int = 1
val b = 1 // 系统自动推断变量类型为Int
val c: Int // 如果不在声明时初始化则必须提供变量类型
c = 1 // 明确赋值
var x = 5 // 系统自动推断变量类型为Int
x += 1 // 变量可修改
字符串模板:PHP里双引号中得$name可以识别为变量
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值:
var a = 1
// 初始化a=1
val s1 = "a is $a"
//初始化常量s1,嵌套了一个$a等同于 val s1="a is 1"
a = 2
// 重新复制a
val s2 = "${s1.replace("is", "was")}, but now is $a"
//输出: a was 1 ,but now is 2
//replace("原字符串","新字符串")
// ${} 返回里面变量执行方法后的值
区间
fun main(args: Array<String>) {
print("循环输出:")
for (i in 1..4) print(i) // 输出“1234”
println("\n----------------")
print("设置步长:")
for (i in 1..4 step 2) print(i) // 输出“13”,第二次输出位数移动两位
println("\n----------------")
print("使用 downTo:")
for (i in 4 downTo 1 step 2) print(i) // 输出“42”,倒序输出 ,指定step为2
println("\n----------------")
print("使用 until:")
// 使用 until 函数排除结束元素
for (i in 1 until 4) { // i in [1, 4) 直到4停止输出,开区间
print(i)
}
println("\n----------------")
}
NULL空指针处理
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
fun main(args: Array<String>) {
if (args.size < 2) {
print("Two integers expected")
return
}
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)
}
}
类型判断与转换:js用typeof判断类型,用(string)等强制转换类型
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
}
基本数据类型
Byte、Short、Int、Long、Float、Double(字符串独立的数据类型)
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
//这个操作没见过,_下划线是会被自动去除吗?
比较两个数字
Kotlin 中没有基础数据类型,只有封装的数字类型,你每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。数字类型也一样,所有在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。
在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。——菜鸟教程
这里说说JS,JS里面数据类型都有两种方法初始化,一种就是对象的实例化,另一种就是原始值的方法。
fun main(args: Array<String>) {
val a: Int = 10000
println(a === a) // true,值相等,对象地址相等
//创建新的对象把引用a的字面量给boxedA,但是内存地址是不一样的
val boxedA: Int? = a//初始化boxedAInt:?可以为空,否则需要判断是否为空
val anotherBoxedA: Int? = a
//同上,新对象,字面量一样,但是内存地址不一致
//虽然经过了装箱,但是值是相等的,都是10000
println(boxedA === anotherBoxedA) // false,值相等,对象地址不一样
println(boxedA == anotherBoxedA) // true,值相等
}
类型转换:name.toInt()/String/
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b // 错误
我们可以代用其toInt()方法。
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b.toInt() // OK
数据类型
- 字符 char
fun decimalDigitValue(c: Char): Int { if (c !in '0'..'9') throw IllegalArgumentException("Out of range") return c.toInt() - '0'.toInt() // 显式转换为数字 }
- 数组
fun main(args: Array<String>) { //[1,2,3] val a = arrayOf(1, 2, 3) //[0,2,4],array函数 val b = Array(3, { i -> (i * 2) }) //参数一是指定长度,参数二是对象,i指针默认指向0 //对象实例化 //读取数组内容 println(a[0]) // 输出结果:1 println(b[1]) // 输出结果:2 }
- 字符串
for (c in str) { println(c) } //循环遍历每一个字符 fun main(args: Array<String>) { val text = """ 多行字符串 多行字符串 """ println(text) // 输出有一些前置空格,多行字符 } fun main(args: Array<String>) { val text = """ |多行字符串 |菜鸟教程 |多行字符串 |Runoob """.trimMargin() println(text) // 前置空格删除了 } 默认 | 用作边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")。
条件语句
// 作为表达式
val max = if (a > b) a else b
//使用区间
fun main(args: Array<String>) {
val x = 5
val y = 9
if (x in 1..8) {
println("x 在区间内")
}
}
//switch基本一样
//when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x 不是 1 ,也不是 2")
}
}
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")
}
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
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 (item in collection) print(item)
//如果你想要通过索引遍历一个数组或者一个 list,你可以这么做:
for (i in array.indices) {
print(array[i])
}
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
返回和跳转
Kotlin 有三种结构化跳转表达式:
- return。默认从最直接包围它的函数或者匿名函数返回。
- break。终止最直接包围它的循环。
- continue。继续下一次最直接包围它的循环。
Break 和 Continue 标签
在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@、fooBar@都是有效的标签。 要为一个表达式加标签,我们只要在其前加标签即可。
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
标签处返回
fun foo() {
ints.forEach {
if (it == 0) return
print(it)
}
}
//foo 中返回
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}
//lambda返回
fun foo() {
ints.forEach {
if (it == 0) return@forEach
print(it)
}
}
//同上
//或者,我们用一个匿名函数替代 lambda 表达式。 匿名函数内部的 return 语句将从该匿名函数自身返回
fun foo() {
ints.forEach(fun(value: Int) {
if (value == 0) return
print(value)
})
}
//当要返一个回值的时候,解析器优先选用标签限制的 return,即
return@a 1
类与对象
类定义:
val site = Runoob() // Kotlin 中没有 new 关键字
类属性定义
class Runoob {
var name: String = ……
var url: String = ……
var city: String = ……
}
实例:kotlin中,初始化变量时,“=”,系统会自动调用setter()函数,field关键字
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 no: Int = 100 get() = field // 后端变量 set(value) { if (value < 10) { // 如果传入的值小于 10 返回该值 field = value } else { field = -1 // 如果传入的值大于等于 10 返回 -1 } }
主构造器
主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。
class Person constructor(firstName: String) { init { println("FirstName is $firstName") } }
创建一个 Runoob类,并通过构造函数传入网站名:
class Runoob constructor(name: String) { // 类名为 Runoob // 大括号内是类体构成 var url: String = "http://www.runoob.com" var country: String = "CN" var siteName = name init { println("初始化网站名: ${name}") }
constructor (name: String, alexa: Int) : this(name) { println("Alexa 排名 $alexa") }
//次构造函数
fun printTest() { println("我是类的函数") } } fun main(args: Array<String>) { val runoob = Runoob("菜鸟教程") println(runoob.siteName) println(runoob.url) println(runoob.country) runoob.printTest() }
嵌套类和内部类在使用时的区别
(1)创建对象的区别
var demo = Outter.Nested()// 嵌套类,Outter后边没有括号 var demo = Outter().Inner();// 内部类,Outter后边有括号
引用外部类的成员变量的方式不同
先来看嵌套类:
class Outer { // 外部类 private val bar: Int = 1 class Nested { // 嵌套类 var ot: Outer = Outer() println(ot.bar) // 嵌套类可以引用外部类私有变量,但要先创建外部类的实例,不能直接引用 fun foo() = 2 } }
再来看一下内部类(引用文章中代码):
class Outer { private val bar: Int = 1 var v = "成员属性" /**嵌套内部类**/ inner class Inner { fun foo() = bar // 访问外部类成员 fun innerTest() { var o = this@Outer //获取外部类的成员变量 println("内部类可以引用外部类的成员,例如:" + o.v) } } }
可以看来内部类可以直接通过 this@ 外部类名 的形式引用外部类的成员变量,不需要创建外部类对象;
继承
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:
class Example // 从 Any 隐式继承
//重写
//在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:
/**用户基类**/
open class Person{
open fun study(){ // 允许子类重写
println("我毕业了")
}
}
/**子类继承 Person 类**/
class Student : Person() {
override fun study(){ // 重写方法
println("我在读大学")
}
}
fun main(args: Array<String>) {
val s = Student()
s.study();
}
Kotlin 数据类与密封类
。。未完待续,持续学习!