类与对象
class Person{
var name = ""
var age = 0
fun eat(){
println(name + "is eating,he is "+ age +"years old")
}
}
fun main(){
val p = Person()
p.name = "jack"
p.age = 19
p.eat()
}
继承
open关键词告诉kotlin编译器,Person允许被继承
Student继承Person,kotlin中使用“:”
open class Person{
}
class Student : Person(){
var sno = ""
var grade = 0
}
主构造函数和次构造函数
学号,年级放入主构造函数中,必须传入构造函数中要求的所有参数。
主构造函数初始化逻辑init结构体
Person()代表初始化的时候调用Person的无参构造函数
class Student(val sno:String,val grade:Int):Person(){
}
val Student = Student("a123",5)
//主构造函数中编写逻辑
class Student(val sno:String,val grade:Int):Person(){
//init结构体
init{
println("sno is"+sno)
println("grade is"+grade)
}
}
有参构造方法
open class Person(val name:String,val age:Int){
...
}
class Student(val sno:String,val grade:Int,name:String,age:Int){
...
}
val student = Student("a123",5,"Jack",19)
次构造函数
class Student(val sno:String,val grade:Int,name:String,age:Int){
//参数name,age赋值初始化
constructor(name:String,age:Int):this("",0,name,age){}
//间接调用主构造函数
constructor():this("",0){}
}
//实例化student
val student1 = Student()
val student2 = Student("jack ma",19)
val student3 = Student("a123",5,"jack ma",19);
接口
Java中继承使用的关键字是extends,实现接口使用的关键字是
implements,而Kotlin中统一使用冒号,中间用逗号进行分隔。
interface Study{
fun readBooks()
fun doHomeWork()
}
class Student(name:String,age:Int):Person(name,age),Study{
override fun readBooks(){
println(name+"is reading")
}
override fun doHomeWork(){
println(name+"is doing HomeWork")
}
}
//调用
fun main(){
val student = Student("alice",5)
doStudy(student)
}
fun doStudy(study:Study){
study.readBooks()
study.doHomeWork()
}
interface Study {
//继承类必须实现
fun readBooks()
//默认实现,继承类可以不用实现
fun doHomework() {
println("do homework default implementation.")
}
}
修饰符 | java | kotlin |
---|---|---|
public | 所有类可见 | 所有类可见 |
private | 当前类可见 | 当前类可见 |
protected | 当前类、子类、同一包路径下的类可见 | 当前类、子类可见 |
default | 同一包路径下的类可见 | 无 |
internal | 无 | 同一模块中的类可见 |
单利
Object Singleton{
}
Object Singleton{
fun singletonTest(){
println("singletonTest is called")
}
}
Lambda编程
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
对集合进行遍历
listOf()函数创建的是一个不可变的集合
只能读取,无法添加,修改,删除。
mutableListOf()可变集合
fun main() {
val list = mutableListOf("Apple", "Banana", "Orange", "Pear", "Grape")
list.add("Watermelon")
for (fruit in list) {
println(fruit)
}
}
val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
for (fruit in set) {
println(fruit)
}
//map
val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
集合的函数式API
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val maxLengthFruit = list.maxBy { it.length }
println("max length fruit is " + maxLengthFruit)
filter函数,过滤集合中的数据。
fun main() {
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
//五个字母以内的水果
val newList = list.filter { it.length <= 5 }
.map { it.toUpperCase() }
for (fruit in newList) {
println(fruit)
}
}
any函数用于判断集合中是否至少存在一个元素满足指定条件,all函数用于判断集合中是否所有元素都满足指定条件。
fun main() {
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
//是否至少存在一个元素满足指定条件
val anyResult = list.any { it.length <= 5 }
//是否所有元素都满足指定条件
val allResult = list.all { it.length <= 5 }
println("anyResult is " + anyResult + ", allResult is " + allResult)
}
线程简化:
Thread {
println("Thread is running")
}.start()
点击事件:
button.setOnClickListener {
}
空指针检查
可空类型系统
Kotlin默认所有的参数和变量都不可为空
在类名的后面加上一个问号。比如:
Int表示不可为空的整型,而Int?就表示可为空的整型;
String表示不可为空的字符串,而String?就表示可为空的字符串。
fun doStudy(study: Study?) {
if (study != null) {
study.readBooks()
study.doHomework()
}
}
判空辅助工具
?.操作符:当对象不为空时正常调用相应的方法,当对象为空时则什么都不做。
fun doStudy(study: Study?) {
study?.readBooks()
study?.doHomework()
}
?:操作符:这个操作符的?右两边都接收一个表达式,如果?边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。
val c = a ?: b
let
?.操作符表示对象为空时什么都不做,对象不为空时就调用let
函数,而let函数会将study对象本身作为参数传递到Lambda表达式中,此时的study对象肯定不为空了,我们就能放心地调用它的任意方法了。
fun doStudy(study: Study?) {
study?.let { stu ->
stu.readBooks()
stu.doHomework()
}
}
fun doStudy(study: Study?) {
study?.let {
it.readBooks()
it.doHomework()
}
}
kotlin的小技巧
字符串内嵌表达式
"hello, ${obj.name}. nice to meet you!"
"hello, $name. nice to meet you!"
al brand = "Samsung"
val price = 1299.99
println("Cellphone(brand=" + brand + ", price=" + price + ")")
println("Cellphone(brand=$brand, price=$price)")
函数的参数默认值
fun printParams(num: Int, str: String = "hello") {
println("num is $num , str is $str")
}
fun main() {
printParams(123)
}
键值对的方式传参:
fun printParams(num: Int = 100, str: String) {
println("num is $num , str is $str")
}
fun main() {
printParams(str = "world")
printParams(str = "world", num = 123)
}
标准函数和静态方法
标准函数:Standard文件中定义的函数
with函数:
-
第一个参数:任意类型对象
-
第二个参数:lambda表达式
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape") val result = with(StringBuilder()) { append("Start eating fruits. ") for (fruit in list) { append(fruit).append(" ") } append("Ate all fruits.") toString() } println(result)
run函数:
run函数的用法和使用场景其实with函数是非常类似的,只是稍微做了一些语法改动而已。首先run函数通常不会直接调用,而是要在某个对象的基础上调用;其次run函数只接收一个Lambda数,并且会在Lambda表达式中提供调用对象的上下文。其他方面和with函数是一样的,包括也会使用Lambda表达式中的最后一行代码作为返回值返回。
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().run {
append("Start eating fruits.
")
for (fruit in list) {
append(fruit).append("
")
}
append("Ate all fruits.")
toString()
}
println(result)
apply函数:
apply函数无法指定返回值,而是会自动返回调用对象本身,其他类似run函数。
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().apply {
append("Start eating fruits.
")
for (fruit in list) {
append(fruit).append("
")
}
append("Ate all fruits.")
}
println(result.toString())
在Android中的使用:
val intent = Intent(context, SecondActivity::class.java).apply {
putExtra("param1", "data1")
putExtra("param2", "data2")
}
context.startActivity(intent)
静态方法:
companion object:在类内部创建伴生类。
class Util {
fun doAction1() {
println("do action1")
}
companion object {
//@JvmStatic
fun doAction2() {
println("do action2")
}
}
}
Util.doAction2()来调用,但是doAction1就不能。
加上@JvmStatic注解就是真正的静态方法。@JvmStatic注解只能加在单例类或companion object中的方法上,如果你尝试加在
一个普通方法上,会直接提示语法错误。
延迟初始化
延迟初始化使用的是lateinit关键字,它可以告诉Kotlin编译器,我会在晚些时候对这个变量
进行初始化,这样就不用在一开始的时候将它赋值为null了
class MainActivity : AppCompatActivity(), View.OnClickListener {
//延迟初始化adapter
private lateinit var adapter: MsgAdapter
override fun onCreate(savedInstanceState: Bundle?) {
...
//判断adapter是否初始化
if (!::adapter.isInitialized) {
adapter = MsgAdapter(msgList)
}
...
}
override fun onClick(v: View?) {
...
adapter.notifyItemInserted(msgList.size - 1)
...
}
}
使用密封类优化代码
密封类的关键字是sealed class
sealed class Result
class Success(val msg: String) : Result()
class Failure(val error: Exception) : Result()
fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> "Error is ${result.error.message}"
}
扩展函数和运算符重载
扩展函数:即使在不修改某个类的源码的情况下,仍然可以打开这个类,向该类添加新的函数。
向哪个类中添加扩展函数,就定义一个同名的Kotlin文件,这样便于你以后查找。当然,扩展函数也是可以定义在任何一个现有类当中的,并不一定非要创建新文件。不过通常来说,最好将它定义成顶层方法,这样可以让扩展函数拥有全局的访问域。
String.kt
fun String.lettersCount(): Int {
var count = 0
for (char in this) {
if (char.isLetter()) {
count++
}
}
return count
}
如上扩展String类,添加获取字符串中字母数据的方法
val count = "ABC123xyz!@#".lettersCount()
运算符重载
运算符重载使用的是operator关键字,只要在指定函数的前面加上operator关键字,就可以实现运算符重载的功能了。但问题在于这个指定函数是什么?这是运算符重载里面比较复杂的一个问题,因为不同的运算符对应的重载函数也是不同的。比如说加号运算符对应的是plus()函数,减号运算符对应的是minus()函数。
class Money(val value: Int)
class Money(val value: Int) {
operator fun plus(money: Money): Money {
val sum = value + money.value
return Money(sum)
}
operator fun plus(newValue: Int): Money {
val sum = value + newValue
return Money(sum)
}
}
val money1 = Money(5)
val money2 = Money(10)
val money3 = money1 + money2
val money4 = money3 + 20
println(money4.value)
高阶函数详解
如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。
函数类型基本规则:
(String, Int) -> Unit
(接收参数)->返回值类型(没有返回值使用unit,类似java void)
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
val result = operation(num1, num2)
return result
}
fun plus(num1: Int, num2: Int): Int {
return num1 + num2
}
fun minus(num1: Int, num2: Int): Int {
return num1 - num2
}
fun main() {
val num1 = 100
val num2 = 80
//::plus ::minus 表示plus和minus参数作为参数传递给num1AndNum2
val result1 = num1AndNum2(num1, num2, ::plus)
val result2 = num1AndNum2(num1, num2, ::minus)
println("result1 is $result1")
println("result2 is $result2")
}
//使用lambda表达式来实现
fun main() {
val num1 = 100
val num2 = 80
val result1 = num1AndNum2(num1, num2) { n1, n2 ->
n1 + n2
}
val result2 = num1AndNum2(num1, num2) { n1, n2 ->
n1 - n2
}
println("result1 is $result1")
println("result2 is $result2")
}
内联函数(高阶函数的原理)
Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样也就不存在运行时的开销了。
内联函数和非内联函数还有一个重要的区别,那就是内联函数所引用的Lambda表达式中是可以使用return关键字来进行函数返回的,而非内联函数只能进行局部返回。
infix函数
infix函数是不能定义成顶层函数的,它必须是某个类的成员函数,可以使用扩展函数的方式将它定义到某个类当中;其次,infix函数必须接收且只能接收一个数,至于参数类型是没有限制的。只有同时满足这两点,infix函数的语法糖才具备使用的条件。
/**
* infix函数
*
*/
infix fun <T> Collection<T>.has(element: T) = contains(element)
fun main() {
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
if (list has "Banana") {
println("right")
}
}