数据类型
只有引用类型,处于高性能需求,Kotlin编译器会把Int,Byte等在Java字节码中改变成基本数据类型
类型推断:可省略对象的类型(编译的时候会自动检测)
val s = "编译时自动检测出是String类型"
String模板
支持在字符串的引号内放入变量值,使用$或${ }
关键字
const 用于修饰常量的,const val 相当于Java中的 static final
lateinit 延时初始化 obj.isInitialized判断延迟初始化对象是否初始化了
in 关键字判断是否在某个范围内,或者用于遍历范围内的对象 is 判断是否属于某 类型
条件表达式
when/if else 都可以当作表达式和语句使用,当做表达式的时候都需要需要else语句,代码中有else if分支,都建议改成when表达式
range表达式,结合通过in关键字来定义范围
区间
var a = 1..100 // [1,100]
var a = 1 until 100 // [1,100)
a.reversed() //将数字倒转,相当于 a = 100 .. 0
for循环
var nums = 1 .. 100
for(num in nums) //遍历每个值 1,2,3 ... 100
for(num in nums step 2) //每间隔2个步长遍历 1,3,5,7 ... 99
for(num in nums downTo 0) //递减直到值为0 , 100,99 ... 0
符号
? //当前对象可以为空,加在变量名后,系统在任何情况不会报它的空指针异常
?. //安全的调用某个对象的方法,如果对象为null,则不再调用该方法,直接返回一个null对象,而不会抛空指针异常
var nameStr : String = user?.name
类似Java的:
String namseStr = null;
if (user != null){
namseStr = user.getName();
}
?: //Elvis运算符,安全的调用某个对象的方法,如果对象为null,则返回一个默认的值
var nameStr : String = user?.name?:"hxx";
类似于Java的:
String nameStr ;
if (user != null && user.getName() != null){
nameStr = user.getName();
} else {
nameStr = "hxx";
}
!! //当前对象不为空时执行代码,加在变量名后,如果对象为空,系统报空指针异常NullPointerException
var nameStr = user!!.name!!.trim()
//这样写的前提是,我们确定user和user.name不会为null,否则就像我们Java中获取bean里面的值,却没有判空一样,容易抛异常
:: //把一个方法当做一个参数,传入另一个方法中
例如:
fun money (name: String, y: Int): String {
return "${name}通过自己的努力,股票赔了${y}¥"
}
fun someBody (name: String, age: Int): String {
return "${name}今年刚刚年满${age}岁"
}
//前两个参数为变量,第三个参数为方法
fun test( x: String, y: Int ,methed: (a: String, b: Int) -> String) : String{
return methed(x ,y )
}
//调用:通过test方法,第三个参数传入想要执行的方法名,传入的方法不同,得到的结果不同
var a = test("张三",10000000, :: money)
var b = test("小明",18, :: someBody)
println(a)
println(b)
//结果
张三通过自己的努力,股票赔了10000000¥
小明今年刚刚年满18岁
//效果相当于
var a = money("张三",10000000)
var b = someBody("小明",18)
集合关系图(图片在网上找的)
list.indices通过索引遍历数组或集合,in关键字可用于遍历集合
函数
格式:可见性修饰符 函数声明关键字 函数名 函数入参 返回值类型
private fun methdName (param :String) : Object { }
Unit函数:没有返回值的函数叫Unit函数,它的返回类型为Unit,可以省略(和Java中的Void有点类似)
TODO函数:它的任务就是抛异常,返回的是Nothing类型
默认参数、具名参数
默认参数:于Java不同,kotlin函数可以指定某个参数的默认值,若该参数的值为默认值时,该参数可以不用传入,其他参数以
例如:
fun test(a : Int = 10, b : Int) : Int{
return a * b;
}
var c = test(b = 8)//c为80
fun test(a : Int = 7) : Int{
return a % 2;
}
var c = test()//c为1
具名参数:变量名 = 值 的方式传入即可(其中变量名为函数声明时的参数名字,又叫具名参数)。它的好处是不应关注传入参数的顺序,减少了函数的重载。
例如:
fun test(name:String, age:Int, address:String){
//method body
}
//test
test(address = "BeiJing", age = 18, name = "Hxx")
反引号函数
Kotlin可以使用空格、特殊字符和关键字对函数命名,但是函数名需要用一对反引号括起来
private fun `h 函数 is`(){}
fun `in`():Int(){}
高阶函数
把一个函数作为参数或者返回值 的函数
上面例子中的test方法就属于高阶函数,下面我们介绍下集合中常用的高阶函数:
maxBy{} 最大值,{}里面条件是某个属性值,返回该属性是最大值的对象
minBy{} 最小值,{}里面条件是某个属性值,返回该属性是最小值的对象
filter{} 过滤特定条件的集合,{}里的条件是布尔表达式,返回过滤后的集合
map{} 根据条件,把某个属性映射成一个新的对象的集合,并返回
any{} 根据条件判断,满足条件返回true,否则返回false
count{} 根据判断条件,返回符合该条件下对象的个数
find{} 根据条件查找第一个满足条件的对象,并返回
groupBy{it.属性} 根据条件(对象的某个属性)将集合中不同值的对象分组并返回该集合
groupBy{it.属性} .get{属性A} 在groupBy{it.属性}返回的集合中,返回该属性值为"属性A"的集合
data class Student(var name: String, var address: String, var age: Int, var sex: String)
val classOne = listOf<Student>(
Student("李宁", "北京", 43, "男"),
Student("天霸", "上海", 13, "女"),
Student("动霸", "苏州", 16, "男"),
Student("动霸", "上海", 33, "男"),
Student("竹竿", "苏州", 18, "男"),
Student("超甜", "上海", 21, "女"),
Student("胖子", "北京", 27, "男"),
Student("麦霸", "苏州", 19, "男")
)
fun main() {
println("maxBy--> "+classOne.maxBy { it.age })//年龄最大的对象
println("minBy--> "+classOne.minBy { it.age })//年龄最小的对象
println("filter--> "+classOne.filter { it.address == "苏州" })//过滤出address == "苏州" 的所有对象
println("any--> "+classOne.any { it.address == "苏州" })//是否有address == "苏州"的对象,返回ture
println("map--> "+classOne.map { "(${it.name},${it.sex})"})//生成一个只有名字和性别的集合
println("count--> "+classOne.count { it.age in 18..20 })//年龄在[18,20]之间的个数
println("find--> "+classOne.find { it.address == "上海" })//第一个上海的对象
println("groupBy--> "+classOne.groupBy { it.sex })//以性别分组,返回各个组的数据
}
结果
maxBy--> Student(name=李宁, address=北京, age=43, sex=男)
minBy--> Student(name=天霸, address=上海, age=13, sex=女)
filter--> [Student(name=动霸, address=苏州, age=16, sex=男), Student(name=竹竿, address=苏州, age=18, sex=男), Student(name=麦霸, address=苏州, age=19, sex=男)]
any--> true
map--> [(李宁,男), (天霸,女), (动霸,男), (动霸,男), (竹竿,男), (超甜,女), (胖子,男), (麦霸,男)]
count--> 2
find--> Student(name=天霸, address=上海, age=13, sex=女)
groupBy--> {男=[Student(name=李宁, address=北京, age=43, sex=男), Student(name=动霸, address=苏州, age=16, sex=男), Student(name=动霸, address=上海, age=33, sex=男), Student(name=竹竿, address=苏州, age=18, sex=男), Student(name=胖子, address=北京, age=27, sex=男), Student(name=麦霸, address=苏州, age=19, sex=男)], 女=[Student(name=天霸, address=上海, age=13, sex=女), Student(name=超甜, address=上海, age=21, sex=女)]}
函数递归:尾递归优化
我们在调用递归函数时sum(100000),计算机由于递归次数太多,会报错栈溢出:StackOverflowError
fun sum (n : Int) : Int{
if(n == 1){
return 1
}else {
return n + sum(n - 1)
}
}
在Java中,当出现这种问题,我们毫无办法,kotlin中则提出了尾递归的解决办法
fun sum (n : Int, result:Int) : Int{
if(n == 0){
return 0
}else {
return sum(n - 1, result + n)
}
}
//调用
var result = 0
sum(10000,result)
open
open修饰的类,可以被继承,如果想要里面的方法或者属性,能够被重写的话,也需要用open来修饰
另外,abstract修饰的抽象类属性和方法,默认都是可以被重写的,不需要显示声明open
可见性修饰符
private,protected,internal,public
public: 默认的修饰符,所有地方都可见
internal: 在相同模块内都可见(模块:指Module,Maven项目,Library等)
protected: 在自己所在的文件内以及子类中可见,只能修饰接口和类中的成员,不能修饰顶层声明
private : 只能被自己所在的文件可见,且外部类不能访问内部类的 private 成员
成员:成员变量、成员方法、内部类、内部接口;
局部声明:局部变量、函数和类不能有可见性修饰符;
包或顶层声明:Java中只能是类、接口,而kotlin的顶层成员,可以是类、接口、函数、属性。
例如:
test.kt文件中,可以不需要类和接口的包裹,直接写函数或属性,调用时 文件名.成员 的形式:
package com.mytest
var x = 0
val URL = "http://www.com/"
fun sum (x:Int ,y :Int) :Int{
return x + y
}
protected class A (){
...
}
...
静态类 object、companion object
1.object:用于修饰静态类或者静态内部类,类似于Java中的static修饰符,例如
object A{
fun action(){
println("我就是我,天下无双")
}
}
//调用
A.action()
2.companion object:伴生对象,伴生对象在类中只能有一个。一般在一个非静态类中来声明一个伴生对象,里面的方法和属性都是静态的,可以直接用 类名.方法/属性名 来调用,且伴生对象中,只能调用静态的方法或属性。
class A {
fun test1(){
println("test1")
}
companion object {
fun test2(){
println("test2")
}
}
}
//调用
A().test1()
A.test2()
密封类(印章类):seales class :子类类型有限的class
1.密封类里类似于枚举,在类中只能声明有限个内部类,且必须是它的子类,但每个子类可以有多个对象;
2.密封类的所有子类都必须与蜜蜂类在同一个文件中;
3.密封类没有构造函数,不可以直接实例化,只能实例化内部的子类;
4.密封类子类的子类可以在其他文件中定义
sealed class SealedClass{
class Son1() : SealedClass()
class Son2() : SealedClass()
fun eat() {
println("美食")
}
}
//调用
var son1 : SealedClass = SealedClass.Son1()
son1.eat()
接口的代理、委托模式(待完善)
关键字 by
interface Human{
fun work()
}
object Worker : Human{
fun work(){
println("我是工人,勤劳的小蜜蜂")
}
}
class Boss : Human by Worker{
fun work(){
println("我是Boss,有钱不想干活")
Worker.work()
}
}
//调用时,只需调用Boss的work方法,但工作还是Worker完成的
//Boss().work()
//结果
//我是Boss,有钱不想干活
//我是工人,勤劳的小蜜蜂
DSL(领域特定语言)
两个前提:
1.扩展函数
2.中缀表达式
扩展函数加上 infix 关键字,就可以用空格代替“.”区调用函数