变量与基本类型
变量
- var 可变
- val 常量,类似const
声明方式
var 变量名:变量类型
var id:Int
var id:Int = 12 // 直接指明类型
var id = 12 // 自动推断类型
全局变量
直接写在文件中
出厂自带getter、setter
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
如果不动getter和setter,默认只具备最具出的功能如下
var name: String = "小明"
/*类似这样的函数
fun getName() : String { //编译时自动生成了对应变量的get函数
return this.name
}
fun setName(name: String) { //编译时自动生成了set函数
this.name = name;
}
*/
fun main() {
println(name)
println(getName()) //获取name时本质上是调用getName函数
}
如果想修改这两个函数,可以这么做
var str: String = "尊嘟假嘟"
set(s) {
field = s + s //field表示变量本身的值
}
get() = field + "666"
fun main(){
println(str)
}
流程控制语句
选择结构if-else
大部分用法和其他语言一个鸟样
奇奇怪怪的用法,代替三目运算符
默认将if各分支的最后一行作为返回值
fun main() {
val score = 2
//这里判断socre是否大于60,是就得到Yes,否就得到No,并且可以直接赋值给变量
val res = if (score > 60) "Yes" else "No"
}
直接把返回值丢给res
fun main() {
val score = 2
val res = if (score > 60) {
println("不错啊期末没挂科")
"Yes" //代码块默认最后一行作为返回结果
} else {
println("不会有人Java期末还要挂科吧")
"No"
}
}
选择结构when(疑似代替switch分支)
爪洼和咳特灵对比
switch(目标){
case 匹配值1:
代码1...;
break;
case 匹配值2:
代码2...;
break;
default:
代码3...;
}
when (目标) {
匹配值1 -> 代码1... //我们需要传入一个目标,比如变量,或是计算表达式等
匹配值2 -> 代码2... //如果目标的值等于我们这里给定的匹配值,那么就执行case后面的代码
else -> {
代码3... //如果以上条件都不满足,就进入else中(可以没有),类似于之前的if-elseif-else
}
}
如果when分支能把所有情况全部包含(编译器也能推断出),那么可以不含else,不然就老老实实写else
同理,在每个分支的最后一行写个值就能当成这个分支的返回值,直接丢给numericValue
fun main() {
val c = 'A'
val numericValue = when (c) {
'B' -> 0
'A' -> 1
else -> 2
}
}
如果我想像Java一样实现这种情况呢
String agentType=null;
switch(agentId){
case 1:
case 2:
case 3:
case 4:
case 5:
agentType = "Nao";
case 6:
case 7:
case 8:
agentType = "Nao2";
default:
agentType = "NaoToe";
}
用Kotlin写是这样的
String agentType = when(agentId){
1,2,3,4,5 -> "Nao"
6,7,8 -> "Nao2"
else -> "NaoToe"
}
你可能觉得这样还是太麻烦了,数字得一个个列举,那么好,使用..来连接两个端点表示范围
String agentType = when(agentId){
in 1..5 -> "Nao"
in 6..8 -> "Nao2"
else -> "NaoToe"
}
谁能用Kotlin写个RoboCup3D底层(bushi)
循环结构for
for循环基本结构
for (遍历出来的单个目标变量 in 可遍历目标) 循环体
可遍历目标可以是以下
- 数组
- 区间
- 任何实现了运算符重载函数iterator的类
举栗
$i在字符串中引用循环变量,非常像shell(bushi)
fun main() {
for (i in 1..3) //这里直接写入1..3表示1~3这个区间
println("第$i次循环")
}
指定步长
fun main() {
for (i in 1..3 step 2) //这里直接写入1..3表示1~3这个区间
println("第$i次循环")
}
倒着走(downTo)
fun main() {
for (i in 10 downTo 1 step 2) {
println(i)
}
}
给循环打标签
continue与break同理
fun main() {
for (i in 1..3) {
for (j in 1..3) {
if (i == j) break //break只会终结内部循环
println("$i, $j")
}
}
}
打标签就可以实现在内部循环对外部循环进行控制
fun main() {
outer@ for (i in 1..3) { //在循环语句前,添加 标签@ 来进行标记
inner@ for (j in 1..3) {
if (i == j) break@outer //break后紧跟要结束的循环标记,当i == j时终止外层循环
println("$i, $j")
}
}
}
循环结构while
while循环基本结构
while(循环条件) 循环体
do{
循环体
}while(循环条件)
其他用法基本没什么差别,也可以打标签
函数
函数基本格式
fun 函数名(参数1:类型,参数2:类型...):返回值类型{
}
函数参数以及类型标注
fun sum(a: Int, b: Int) : Int {
return a + b //使用return语句将结果返回
}
如果没有返回值也可以不写返回值类型
fun main() {
test("香翅捞饭")
}
fun test(str:String) {
println(str)
}
参数缺省值可以像C++,py那样
fun main() {
test() //调用函数时,如果对应参数有默认值,可以不填
}
fun test(text: String = "我是默认值"){
println(text)
}
若有多个可以缺省的,且不都在参数列表最后,可以像py那样直接用键值对指定要填写哪个参数
fun main() {
test(b = 3) //这里如果只想填写第二个参数b,我们可以直接指定吧实参给到哪一个形参
test(3) //这种情况就是只填入第一个实参
}
fun test(a: Int = 6, b: Int = 10): Int {
return a + b
}
超级简化
fun test(a: Int = 6, b: Int = 10): Int = a + b //函数的结果直接缩减为 = a + b 效果跟之前是一样的
fun test(a: Int = 6, b: Int = 10) = a + b //返回类型可以自动推断,这里可以吧返回类型省掉
函数嵌套
fun outer(){
fun inner(){
}
inner()
}
函数重载
fun test() = println("A")
fun test(str: String) = println("B") //参数列表不一致
递归尾优化
tailrec fun test(n: Int, sum: Int = 0): Int {
if(n <= 0) return sum //到底时返回累加的结果
return test(n - 1, sum + n) //不断累加
}
编译之后的样子
例:斐波那契
tailrec fun fib(n: Int, prev: Int = 0, next: Int = 1): Int {
return if (n == 0) prev else fib(n - 1, next, prev + next) //从0和1开始不断向后,直到n为0就返回
}
常用函数
fun main(){
print()
println()
val text = readln()
1.6.pow(4.0) //我们可以使用pow方法直接计算a的b次方
abs(-1); //abs方法可以求绝对值
max(19, 20); //快速取两个数的最大值
min(2, 4); //快速取最小值
sqrt(9.0); //求一个数的算术平方根
//这里我们可以直接使用库中预设好的PI
sin(PI / 2); //求π/2的正弦值,这里我们可以使用预置的PI进行计算
cos(PI); //求π的余弦值
tan(PI / 4); //求π/4的正切值
asin(1.0); //三角函数的反函数也是有的,这里是求arcsin1的值
acos(1.0);
atan(0.0);
ln(E) //e为底的对数函数,其实就是ln,我们可以直接使用Math中定义好的e
log10(100.0) //10为底的对数函数
log2(8.0) //2为底的对数函数
//利用换底公式,我们可以弄出来任何我们想求的对数函数
val a = ln(4.0) / ln(2.0) //这里是求以2为底4的对数,log(2)4 = ln4 / ln2
println(a)
ceil(4.5) //通过使用ceil来向上取整
floor(5.6) //通过使用floor来向下取整
}
高阶函数与lambda表达式
将函数看成了变量
var func0: (Int) -> Unit //这里的 (Int) -> Unit 表示这个变量存储的是一个有一个int参数并且没有返回值的函数
var func1: (Double, Double) -> String //同理,代表两个Double参数返回String类型的函数
现在可以把函数当成变量用了(作为函数参数传递)
fun test(other: (Int) -> String){
println(other(1)) //这里提供的函数接受一个Int参数返回string,那么我们可以像普通函数一样传入参数调用它
}
给lambda函数起个别名
typealias HelloWorld = (String) -> Double // 使用类型别名关键字定义HelloWorld
fun main() {
var func: HelloWorld // 创建一个HelloWorld类型的变量
}
函数的具体表示
可以先定义一个正常的函数,然后让一个变量等于他,两个函数类型要一致
fun main() {
var func: (String) -> Int = ::test //使用双冒号来引用一个现成的函数
}
fun test(str: String): Int {
return 666
}
可以直接用匿名函数(很像js啊),那么匿名函数就有了名字func
fun main() {
val func: (String) -> Int = fun(str: String): Int {
println("这是传入的内容$str")
return 666
}
}
用lambda定义函数
一个Lambda表达式只需要直接在花括号中编写函数体即可
默认情况下,如果函数只有一个参数,我们可以使用it代表传入的参数
函数体最后一行的值作为返回值
fun main() {
var func: (String) -> Int = {
println("这是传入的参数$it")
666
}
func("HelloWorld!")
}
如果函数有好几个参数呢?
fun main() {
val func: (String, String) -> Unit = { a, b -> // 手动添加两个参数这里的形参名称
println("这是传入的参数$a, 第二个参数$b") // 直接使用上面的形参
}
val func2: (String, String) -> Unit = { _, b ->
println("这是传入的第二个参数$b") // 假如这里不使用第一个参数,也可以使用_下划线来表示不使用
}
func("Hello", "World")
}
如果函数的最后一个参数是函数类型的,那么调用时,其他非参数类写在括号里,最后一个参数可以直接lambda,比如
fun main() {
test("小明") {
println("用中文说$it")
}
}
fun test(name:String,speak:(String)->Unit){
print(name)
speak("我是sb")
}
关于lambda中的return
在Lambda中没有办法直接使用return语句返回结果,而是需要用到之前的标签
fun main() {
val func: (Int) -> String = test@{
//比如这里判断到it大于10就提前返回结果
if(it > 10) return@test "我是提前返回的结果"
println("我是正常情况")
"收到的参数为$it"
}
test(func)
}
fun test(func: (Int) -> String) {
println(func(66))
}
如果是函数调用的尾随lambda表达式,默认的标签名字就是函数的名字
fun main() {
testName { //默认使用函数名称
if(it > 10) return@testName "我是提前返回的结果"
println("我是正常情况")
"收到的参数为$it"
}
}
fun testName(func: (Int) -> String) {
println(func(66))
}
内联函数
和之前的C++内联函数差不多
内联函数的return
直接返回main
fun main() {
test { return@main } //标签可以直接指定为外层函数名称main来提前终止整个外部函数
println("调用上面方法之后")
}
inline fun test(func: (String) -> Unit){
func("HelloWorld")
println("调用内联函数之后")
}
返回调用它的test函数
fun main() {
test { return@test } //这样就只会使test返回,而不会影响到外部函数了
println("调用上面方法之后")
}
inline fun test(func: (String) -> Unit){
func("HelloWorld")
println("调用内联函数之后")
}
多个高阶函数的情况
有些时候,可能一个内联的高阶函数中存在好几个函数参数,但是我们希望其中的某一个函数参数不使用内联,能够跟之前一样随意当做变量使用
fun main() {
test({ println("我是一号:$it") }, { println("我是二号:$it") })
}
//在不需要内联的函数形参上添加noinline关键字,来防止此函数的调用内联
inline fun test(func: (String) -> Unit, noinline func2: (Int) -> Unit){
println("这是一个内联函数")
func("HelloWorld")
var a = func2 //这样就不会报错,但是不会内联了
func2(666)
}