Kotlin基础
一.Kotlin介绍
1.诞生
2.Kotlin与JVM
3.为什么学习kotlin?
4.Kotlin跨平台特性
二.变量+常量+类型+条件语句+函数
1.声明变量
2.内置数据类型
没有基本数据类型,只有引用类型,但是编译器会将部分引用类型编程成基本数据类型,看编译后的字节码文件。
3.只读变量
4.类型推断
5.编译时常量
6.条件语句:表达式
7.String模板
8.函数
(1)函数头
(2)函数参数
(3)Unit函数
三.匿名函数+函数类型+高阶函数(重点)
1.匿名函数
2.函数类型与隐式返回
函数类型格式:(输入参数类型)->返回类型
- 无输入参数也无返回值:() -> Unit
- 无输入参数返回值为String类型:()->String
- 输入参数为String类型,返回值是String类型:(String)->String
3.函数参数
4.it关键字
5.匿名函数类型推断(简写代码,只有匿名函数有,具名函数没有)
6.lambda表达式(重点面试必问)
7.高阶函数(重点面试必问)
参数为函数类型或者返回值为函数类型的函数为高阶函数
(1)定义参数为函数类型的函数
fun main(){
//1。匿名函数
val printUserChinese = {username:String,age:Int->
"姓名=$username 年龄=$age"
}
val printUserEnglish = {username:String,age:Int->
"name=$username age=$age"
}
//2。将匿名函数作为参数
println(show("姚甜雪",18,printUserChinese))
println(show("姚甜雪",18,printUserEnglish))
//3。简写
println(show("姚甜雪",19,{ username: String, age: Int ->
"简写username=$username 简写age=$age"
}))
//4。最终简写
println(show("姚甜雪",19) { username: String, age: Int ->
"简写username=$username 简写age=$age"
})
//5。温故it
var countL = "helloll".count() {
it == 'l'
}
println(countL)
}
//printUser:参数名称 (String,Int)->String:参数类型是函数 输入参数是String,Int,输出参数是String
fun show(username:String,age:Int,printUser:(String,Int)->String):String{
return printUser(username,age)
}
(2)定义返回值为函数类型的函数
fun main(){
//调用高阶函数
val result = method("张三")
println(result)
val resultStr = result.invoke()
println(resultStr)
//调用高阶函数
val result1 = method1("张三")
println(result1)
val result11 = result1.invoke()
println(result11)
//调用高阶函数
val result2 = method2("张三")
println(result2)
val result22 = result2.invoke(34)
println(result22)
//调用高阶函数
val result3 = method3("张三")
println(result3)
val result33 = result3.invoke(34,"八维")
println(result33)
}
//参数:name
//返回值:()->String 函数类型
fun method(name:String):()->String{
return {
"我是高阶函数的返回的函数数据 $name"
}
}
//参数:name
//返回值:()->Unit 函数类型 无参数无返回值
fun method1(name: String):()->Unit{
return{
println("我是高阶函数的返回的函数数据 $name")
}
}
//参数:name
//返回值:(age:Int)->String 函数类型 有参数int有返回值String
fun method2(name: String):(age:Int)->String{
return {
"我是高阶函数的返回的函数数据 $name 年龄为 $it"
}
}
//参数:name
//返回值:(age:Int,school:String)->String 函数类型 有2个参数有返回值String
fun method3(name: String):(age:Int,school:String)->String{
return { age:Int,school:String->
"我是高阶函数的返回的函数数据 学校$school 年龄为 $age 姓名 $name"
}
}
8.内联函数(重点面试必问)
9.lambda和匿名内部类接口回调
四.null安全与异常
1.null安全
//1.可空性
var str:String? = "abc"
str = null
//2.安全调用操作符
str?.toString()
//3。非空断言操作符
str!!.toString()
//4.使用let函数的安全调用(经常使用):不为null时调用里面的
str?.let {
if(it.isNotBlank()){//不为""
it.capitalize()//转大写
}else{
"abc"
}
}
//5.空合并操作符:如果为空执行右边,如果不为空执行左边
str = str ?: "demo"
println(str)
//6.let和空合并结合使用
str = str?.let {
it.capitalize()
} ?:"demo"
2.异常
五.字符串操作+数字类型+标准库函数
1.字符串操作
/*****字符串******/
//字符串截取
val index = NAME.indexOf("o")
val str = NAME.substring(0 until index )
println(str)
//切割
val data = NAMES.split(",")
println(data[0])
//切割解构语法
val (n1,n2,n3) = NAMES.split(",")
println("$n1 $n2 $n3")
//替换
val strr = "xiaoming,danni,xiaohong!"
val str2 = strr.replace(Regex("[xio]")){
when(it.value){
"x"-> "1"
"i"-> "2"
"o"-> "3"
else->it.value
}
}
println(str2)
//==和===:==判断内容 ===判断地址
val str3 = "Jason"
val str4 = "Jason"
println(str3 == str4)//true
println(str3 === str4)//true
val str33 = "Jason"
val str44 = "jason".capitalize()
println(str3 == str4)//true
println(str3 === str4)//false
//遍历
strr.forEach {
println("$it")
}
2.数字操作
/*****数字类型安全转换******/
//val number:Int = "8.98".toInt()//报错
// println(number)
val number1:Int? = "8.98".toIntOrNull()
println(number1)
/*****Double转Int数字格式化******/
println(8.985678.toInt())
println(8.985678.roundToInt())//四舍五入
val s:String = "%.2f".format(8.985678)//保留2位小树
println(s)
3.标准库函数
标准库函数特点:
(1)所有的对象均可调用标准库函数,因为是泛型扩展函数
扩展函数:String封装部分函数,这些函数不够使用,对String扩展函数
扩展函数:fun String.函数名(){},对String扩展函数,只能String对象调用此函数
泛型扩展函数:fun T.函数名(){} 对所有类的扩展函数,所有对象调用此函数
(2)这些函数都在Standard.kt文件中 :Standard标准
标准库函数总结:
- apply:返回类型是调用者本身+匿名函数中持有的是this==对象本身
- let:返回类型是匿名函数的最后一行+匿名函数中持有的it==对象本身
- run:返回类型是匿名函数的最后一行+匿名函数中持有的this==对象本身
- with(str): with个run基本一样,使用方式不同.返回类型是匿名函数的最后一行+匿名函数中持有的this==对象本身
- also:返回类型是调用者本身+匿名函数中持有的it==str对象本身
- takeIf :true执行后面
- takeUnless :false执行后面
fun main(){
/******takeIf和takeUnless*****/
val result = "yaotianxue".capitalize()
//首字母转大写之前,判断字符串包含yao且长度大于6
//takeIf满足条件才会执行后面操作
val result2 = "yaotianxue"
.takeIf {
it.length > 13 && it.contains("yao")
}
?.capitalize()//首字母转大写
?:"默认值"//如果不满足takeIf条件 默认值
println(result2)
/******apply******/
val file = File("")
file.setWritable(true)
file.setReadable(true)
file.setExecutable(true)
//针对无法链式编程的代码可以用apply
val file2 = File("地址").apply {
setWritable(true)
setReadable(true)
setExecutable(true)
}
/******let:求一个集合中第一个元素后的平方*******/
//不用let函数
val num1 = listOf(3,2,1).first()
val result1 = num1 * num1
println(result1)
//用let函数:节省变量的声明
val result22 = listOf(3,2,1).first().let {
it*it
}
println(result22)
/***also*******/
val str22 = "yaotianxue"
val result33 = str22.also {
println("用户名为:$str22")
}.also {
println("用户名转大写 ${str22.capitalize()}")
}
println(result33)
/***run**/
val str33 = "yaotianxue"
val result44 = str33.run {
str33.length
}
println(result44)
//使用run多个函数调用
val result55 = str33
.run(::isLong)
.run(::show)//将isLong方法的返回数据作为参数传递给show方法
println(result55)
/**with:是run方法的变体**/
val result66 = with(str33,::isLong)
println(result66)
}
fun isLong(name:String):Boolean{
return name.length > 10
}
fun show(isLong:Boolean):String{
return if(isLong) "太长了" else "太短了"
}
fun show2(isLong:Int):String{
return if(isLong>10) "太长了" else "太短了"
}
六.集合:List+Set+Map
0.数组
1.List
(1)List的创建和取值
(2)可变列表
(3)mutator函数
(4)集合遍历
(5)解构
2.Set
(1)Set集合的创建和取值
(2)可变集合
3.Map
(1)Map集合的创建
(2)读取map
(3)遍历
(4)可变集合
七.对象+接口+抽象类
1.类和对象
主构造/次构造/默认参数/初始化代码块
//主构造并设置属性
class Student(var name:String = "默认值",var age:Int ){
//次构造
constructor(name:String) : this(name,18){
}
//初始化代码块
init {
println("初始化代码块,每次在实例化对象的时候调用")
}
}
fun main(){
//创建对象
var student = Student("长孙",34)
println("用户名 ${student.name} 年龄为 ${student.age}")
//设置属性值
student.name = "张三"
student.age = 12
println("用户名 ${student.name} 年龄为 ${student.age}")
//使用次构造创建对象
var student2 = Student("长孙")
println("用户名 ${student.name} 年龄为 ${student.age}")
}
2.继承
类默认是封闭的不能继承,使用open修饰类和方法名,子类可以继承和重写
Animal父类:
open class Animal(val name: String) {
open fun show(){
println("动物的名称 $name")
}
}
Dog子类:
class Dog:Animal("dog") {
override fun show() {
super.show()
println("小狗子类的方法")
}
}
Cat子类:
class Cat:Animal("cat") {
override fun show() {
super.show()
println("小猫子类的方法")
}
}
测试类
fun main(){
val dog = Dog()
val cat = Cat()
//is类型检测
println(dog is Dog)
println(dog is Animal)
//类型转换
eat(dog)
//Any是所有类的超类
}
fun eat(animal: Animal){
animal.show()
}
也可以将Animal定义成抽象类并提供抽象方法
abstract class Animal(val name: String) {
open fun show(){
println("动物的名称 $name")
}
abstract fun test()
}
3.接口:interface
定义接口
interface Eat {
fun eat()
fun drink()
}
Cat实现接口并重写方法
class Cat:Animal("cat"),Eat {
override fun show() {
super.show()
println("小猫子类的方法")
}
override fun eat() {
println(name+"吃东西")
}
override fun drink() {
println(name+"喝东西")
}
}
4.数据类:data修饰
(1)安装插件
(2)定义数据类
(3)数据实体类
data class Goods(
val bannerList: List<String>,
val category_id: Int,
val goods_attribute: String,
val goods_banner: String,
val goods_code: String,
val goods_default_icon: String,
val goods_default_price: Int,
val goods_desc: String,
val goods_detail_one: String,
val goods_detail_two: String,
val goods_sales_count: Int,
val goods_stock_count: Int,
val id: Int
)
5.object关键字
(1)对象声明
将类的声明和定义该类的单例对象结合在一起(即通过object就实现了单例模式)
对象声明中不能包含构造器(包括主构造器和次级构造器)
声明一个单例的类
object ApplicationConfig {
}
反编译出的Java代码
public final class ApplicationConfig {
public static final ApplicationConfig INSTANCE;
private ApplicationConfig() {
}
static {
ApplicationConfig var0 = new ApplicationConfig();
INSTANCE = var0;
}
}
可以在类中初始化一些数据,定义方法
```java
object ApplicationConfig {
init {
println("初始化,只会调用一次,因为ApplicationConfig单例")
}
fun doSomeThing(){
println("做一些事情")
}
}
测试类
fun main(){
ApplicationConfig.doSomeThing()
println(ApplicationConfig)
println(ApplicationConfig)
}
(2)伴生对象
因为在kotlin中是没有static关键字的,也就意味着没有静态方法和静态成员。那么在kotlin中如果想要表达这种概念,可以使用包级别函数(package-level
funcation)和伴生对象(companion object)
使用单例类的写法会将整个类中的所有方法全部变成类似于静态方法的调用方式,而如果我们只是希望让类中的某一个方法变成静态方法的调用方式该怎么办呢?使用伴生对象
语法:
class ClassName {
// 伴生对象名可以省略,默认为Companion,只有代码块里面的是静态的
companion object 伴生对象名 {
// define field and method
}
}
class App {
companion object {
fun getAppContext() {}
}
}
// 反编译出的Java代码
public final class App {
public static final App.Companion Companion = new App.Companion((DefaultConstructorMarker)null);
public static final class Companion {
public final void getAppContext() {
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
(3)对象表达式
对象表达式常用来作为匿名内部类的实现
private val callBack = object : CallBack {
override fun onSuccess() {}
override fun onFailure() {}
}
// 通过对象表达式实现点击事件回调
btn.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
TODO("Not yet implemented")
}
})
有这样一个疑问,在对象声明和伴生对象中,java代码调用总会跟上INSTANCE或者Companion,直观上感觉不太友好,能不能像kotlin代码那样可以直接调用?
答案肯定是可以的。需要使用kotlin自带的注解:@JvmStatic或者@JvmField,这样就可以和kotlin调用保持一致。@JvmStatic既可以修饰属性,也可以修饰方法;而@JvmField只能修饰属性
对于对象声明:
object UserManager {
@JvmStatic
fun saveUser()
}
// 反编译出的Java代码
public final class UserManager {
public static final UserManager INSTANCE;
// 和之前差别在于,这里是静态方法,在java代码中可以直接调用
@JvmStatic
public static final void saveUser() {
}
private UserManager() {
}
static {
UserManager var0 = new UserManager();
INSTANCE = var0;
}
}
// java调用
UserManager.saveUser();
对于伴生对象:
class App {
companion object{
@JvmStatic
fun getAppContext() {}
}
}
// 反编译出的Java代码
public final class App {
public static final App.Companion Companion = new App.Companion((DefaultConstructorMarker)null);
// 和之前差别在于,这里多出了一个静态方法,它调用的是Companion内的getAppContext()
// 这样我们可以不用调用Companion.getAppContext(),而可以直接调用该方法
@JvmStatic
public static final void getAppContext() {
Companion.getAppContext();
}
public static final class Companion {
@JvmStatic
public final void getAppContext() {
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
// java调用
App.getAppContext();
6.注解
定义注解
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
annotation class Table(val tableName:String)
测试
@Table(tableName = "user")
public class User {
}
@Target元注解说明:
@Retention元注解说明:
7.枚举类
定义枚举类
enum class Type {
DOWN,
UP,
MOVE
}
枚举类是闭集,使用when 表达式的时候不需要写else,枚举类就是一种ADT
fun check(type:Type):String{
return when(type){
Type.DOWN ->"下"
Type.UP ->"上"
Type.MOVE ->"移动"
//不需要写else
}
}
8.密封类
(1)密封类是Kotlin中的一个高级类,有如下特点:
密封类是为继承设计的,是一个抽象类;
密封类的子类是确定的,除了已经定义好的子类外,不能再有其他子类。
(2)实现
要声明一个密封类,需要在类名前面添加sealed修饰符。这三个类需要在同一个文件中
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
使用密封类完成when表达式,是一种高级的ADT
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
// 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}
(3)枚举和密封类之间的区别
他们的区别是,枚举只能有一个实例,而密封类的子类可以有多个实例,并且密封类的子类可以携带自己独有的状态参数以及行为方法来记录更多的实现信息以完成更多的功能,这是枚举类所不具备的。
八.延迟初始化和惰性初始化(常用)
1.lateinit延迟初始化(activity变量声明的时候用到)
如何在activity中声明一个成员变量?
Kotlin 通常要求我们在定义成员变量后立即对其进行初始化,因为kotlin中会尽量避免空指针异常,不像java中成员变量都有默认值。以下有2种解决方案
方案1:
方案2:
如果您声明一个类属性而不初始化它,IntelliJ IDEA 编辑器会警告您,并建议添加一个lateinit关键字。注意8大原始数据类型不能延迟初始化
Kotlin 通常要求我们在定义成员变量后立即对其进行初始化。当我们不知道理想的初始值时,这样做似乎很奇怪,尤其是在生命周期驱动的 Android 属性的情况下,比如SP操作:
class MainActivity : AppCompatActivity() {
var sharedPreferences: SharedPreferences = getSharedPreferences("test", MODE_PRIVATE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
运行报错:为什么会报空指针
getSharedPreferences方法是activity的方法,当activity没有实例化成功后就调用此方法则会报空指针异常,怎么解决呢?在oncrete方法中获得SP对象,延迟初始化
2.lazy惰性初始化(activity中获得viewmodel的时候用到,后面结合委托继续讲解)
lazy 惰性初始化的属性初始化操作是提前定义好的 , 在 调用之前自动进行初始化操作 , 如果不调用 , 则不进行初始化 ;
lateinit 延迟初始化的属性初始化操作 , 需要手动进行初始化 , 如果忘了初始化直接调用就会报错 ;
class Hello{
val name by lazy {initName()}
fun initName(): String {
println("初始化 name 属性")
return "Tom"
}
}
fun main() {
// 实例化对象时, name 属性不需要初始化
var hello = Hello()
println("实例对象初始化完毕")
Thread.sleep(1000)
// 在调用 name 属性后, 才初始化 name 属性
println("name = ${hello.name}")
}
为什么后面讲解??
之前获得viewmodel
adertimingViewModel = ViewModelProvider(this).get(AdertimingViewModel::class.java)
改用委托后
val userViewModel: UserViewModel by viewModels()
九.泛型
- 泛型,即 “参数化类型”,将类型参数化,可以用在类,接口,函数上。
- 与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
- 优点:
1.类型安全:通用允许仅保留单一类型的对象。泛型不允许存储其他对象。
2.不需要类型转换:不需要对对象进行类型转换。
3.编译时间检查:在编译时检查泛型代码,以便在运行时避免任何问题。
1.泛型类+泛型接口+泛型函数
(1)泛型类
举例:接口中返回的数据data为泛型T类型的
data class ApiResponse<T>(val code:Int,val message:String,val data:T) {
}
(2)泛型接口
interface IFoodEffect<T>{
fun effect(item:T)
}
//实现接口
class Banana:IFoodEffect<String>{
override fun effect(item: String) {
println(item)//item
}
}
//使用
Banana().effect("常食香蕉有益于大脑,预防神经疲劳,还有润肺止咳、防止便秘")
(3)泛型函数
2.泛型约束
T:Vip,这样写就表示这里只能传入Vip和其子类。这个就类似Java的 <? extends T> 上界通配符
open class Vip(price:Int)
class TApple(var price: Int): Vip(price)
class TFood<T:Vip>(item: T) {
...
}
3.形变
out(协变):它只能出现在函数的输出位置,只能作为返回类型,即生产者。
in(逆变):它只能出现在函数的输入位置,作为参数,只能作为消费类型,即消费者。
默认(不变):如果泛型类既将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用out也不用in。
(1)不变:泛型类型即作为输出又作为参数。
/**
* 泛型接口 不变
*/
interface IUnchanged<T> {
fun originally():T
fun originally(item:T)
}
class BigStore:IUnchanged<String>{
override fun originally(): String {
return "实现泛型接口 不变---返回类型"
}
override fun originally(item: String) {
println("------------不变---传参 ---=="+item)
}
}
fun main() {
println("---------------实现泛型接口------- 不变 默认---------------")
var iUnchanged=BigStore()
println(iUnchanged.originally())
println(iUnchanged.originally("参数"))
}
(2)out(协变)
它只能出现在函数的输出位置,只能作为返回类型,即生产者。作用:可以将子类泛型对象可以赋值给父类泛型对象。
//out
interface IReturn<out T>{
fun effect():T
}
open class Fruit()
class AppleHn():Fruit()
//生产者
class FruitMarket:IReturn<Fruit>{
override fun effect(): Fruit {
println("FruitMarket effect")
return Fruit()
}
}
class AppleHnMarket:IReturn<AppleHn>{
override fun effect(): AppleHn {
println("AppleHnMarket effect")
return AppleHn()
}
}
fun main() {
//out:可以将子类泛型对象(AppleHn)可以赋值给父类泛型对象(Fruit)
var fm:IProduction<Fruit> = FruitMarket()
println(fm.effect())
//am的引用类型是Fruit对象
//但是AppleHnMarket返回的是AppleHn对象。
//这里将AppleHn对象赋值给Fruit对象并返回。
var am:IProduction<Fruit> = AppleHnMarket()
println(am.effect())
}
(3)in(逆变)
它只能出现在函数的输入位置,只能作为参数,即消费者。作用:可以将父类泛型对象可以赋值给子类泛型对象。
//in
interface IConsumer<in T>{
fun spend(t:T)
}
class Animal:IConsumer<Fruit>{
override fun spend(t: Fruit) {
println("Animal spend Fruit")
}
}
class People:IConsumer<AppleHn>{
override fun spend(t: AppleHn) {
println("People spend AppleHn")
}
}
fun main() {
//in:可以将父类泛型对象(Fruit)可以赋值给子类泛型对象(AppleHn)
var fca: IConsumer<AppleHn> = Animal()
fca.spend(AppleHn())
println(fca)
var fcp: IConsumer<AppleHn> = People()
fcp.spend(AppleHn())
println(fcp)
}
4.vararg:泛型类一次只能放一个,如果需要放入多个实例呢?
class BookMany<T : AndroidMany>(vararg item: T) {
var data: Array<out T> = item
}
open class AndroidMany(name: String)
class KotlinMany(var name: String, var price: Int) : AndroidMany(name) {
override fun toString(): String {
return "KotlinS(name='$name', price=$price)"
}
}
class JavaMany(var name: String, var price: Int) : AndroidMany(name) {
override fun toString(): String {
return "JavaS(name='$name', price=$price)"
}
}
fun main() {
var book = BookMany(
KotlinMany("初学者", 18),
KotlinMany("进阶者", 28),
KotlinMany("终结者", 38),
JavaMany("全面者", 35),
)
println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
}
添加完多个对象,如果直接打印book,那么获取到的是个BookMany对象,那么怎么获取book里面的的对象?
重载运算符函数get函数,通过[]操作符取值。
class BookMany<T : AndroidMany>(vararg item: T) {
var data: Array<out T> = item
operator fun get(index:Int) = data[index]
}
fun main() {
var book = BookMany(
KotlinMany("初学者", 18),
KotlinMany("进阶者", 28),
KotlinMany("终结者", 38),
JavaMany("全面者", 35),
)
println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
println(book[0])//KotlinS(name='初学者', price=18)
println(book[2])//KotlinS(name='终结者', price=38)
}
十.扩展
标准库提供的很多功能都是通过扩展函数和扩展属性实现,包含类扩展的标准库文件都是以类名加s后缀命名的,例如Strings.kt,Maps.kt等
1.扩展函数
声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面代码为 MutableList 添加一个swap 函数:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点符号前的对象) 现在,我们对任意 MutableList 调用该函数了:
val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // “swap()”内部的“this”会保存“list”的值
当然,这个函数对任何 MutableList 起作用,我们可以泛化它:
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
2.泛型扩展函数
泛型扩展函数在标准库中随处可见,打开查看let also等
下面自定义泛型扩展函数
fun main(){
val str = "abc"
str.easyPrint()
val num = 12
num.easyPrint()
}
//定义泛型扩展函数
fun <T> T.easyPrint():T{
println(this)
return this
}
3.扩展属性
除了给类能够扩展函数,还能为类扩展属性,给String扩展添加一个属性,该属性统计字符串中有多少个a
val String.numVowels
get() = count{it == 'a'}
fun main(){
val str = "abc"
println(str.numVowels)
}
4.对类的伴生对象进行扩展
在kotlin中我们可以对类的伴生对象进行扩展,下面是最基本的代码实现
fun main(){
Jump.Print("这是对Jump的伴生对象的扩展方法")
}
class Jump{
companion object{}
}
fun Jump.Companion.Print(str:String){
println(str)
}