当前位置:   article > 正文

android java转kotlin + jetpack爬坑日记---持续更新_kotlin jatpack

kotlin jatpack

前言:刚不久前换了份工作,该公司已有的产品即是使用kotlin (少部分代码是java,比如一些第三方库) +jetpack 的方式来写的,所以赶鸭子上架,需要迅速的熟悉代码并运用

上一家公司的项目,因为年头有些久了,使用的java语言编写的,也就一直没有采用kotlin,也没有使用jetpack,所以本文适合已经有java经验开发android app,现在希望转向kotlin + jetpack的读者

我会将在项目实际过程中遇到的各种问题记录于此,方便大家查阅

在这之前,请先查看该网址(GitHub - MindorksOpenSource/from-java-to-kotlin: From Java To Kotlin - Your Cheat Sheet For Java To Kotlin),了解一些基本的java写法转向kotlin的区别,下面的文章中,如果是该网址有的一些写法,则不再列入

该文章持续更新,凡是遇到从java转换到kotlin的问题,就会记录下来,欢迎关注

1.构造函数

这是大家非常熟悉的自定义控件的构造函数,其他类的构造函数也类似

  1. public class MyViewPager extends ViewPager {
  2. public MyViewPager(@NonNull Context context) {
  3. super(context);
  4. }
  5. public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
  6. super(context, attrs);
  7. }
  8. }

kotlin的写法如下:

  1. class MyViewPager : ViewPager { //kotlin中,继承用:
  2. //java写法的构造函数,public MyViewPager()在kotlin中,用constructor关键字代替
  3. /* 这里我写的时候不是很理解,自己写成了这样
  4. * constructor(context : Context){
  5. * super(context)
  6. * }
  7. * 还是习惯了java的写法,用{}来框住方法体,然后在里面写逻辑,但是kotlin这里就直接用:就可以了
  8. * 写法就是这样,大家慢慢适应就好了
  9. */
  10. constructor(context: Context) : super(context) {}
  11. //这里是不是很好奇,为什么AttributeSet后面有个? 在kotlin中,参数后面带个?代表这个参数如果为空的话,并不会抛出空指针异常,同样对应的还有 !!,双感叹号代表这个参数不能为空,如果为空的话,会抛出空指针异常
  12. constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
  13. }

2.回调函数

写了个测试的类,里面定义了一个接口LayoutChangeListener

  1. public class TestInterface {
  2. private LayoutChangeListener listener;
  3. interface LayoutChangeListener{
  4. void MoveUp();
  5. void MoveDown();
  6. }
  7. public void setListener(LayoutChangeListener listener){
  8. this.listener = listener;
  9. }
  10. }

java代码调用如下:

  1. TestInterface testInterface = new TestInterface();
  2. testInterface.setListener(new TestInterface.LayoutChangeListener() {
  3. @Override
  4. public void MoveUp() {
  5. }
  6. @Override
  7. public void MoveDown() {
  8. }
  9. });

kotlin则变成这样:

  1. val testInterface = TestInterface() //这里可以用var,也可以val,val相当于java的 final关键字
  2. //object : 简单粗暴的理解为就是java 的 new xxx
  3. testInterface.setListener(object : LayoutChangeListener {
  4. override fun MoveUp() {}
  5. override fun MoveDown() {}
  6. })

3.LiveEventBus

大家以前都是用EventBus,但它可能造成内存泄漏,现在jetpack推出之后,使用LiveEventBus来替代EventBus,它能感知生命周期,使用观察者模式,具体的原理大家自行百度

先简单说明一下LiveEventBus的用法以及和EventBus的区别

  1. //以下是eventbus的使用方法
  2. EventBus.getDefault().register(this)
  3. @Subscribe(threadMode = ThreadMode.MAIN)
  4. public void onColorEvent(String content){
  5. }
  6. EventBus.getDefault().post("test")
  7. //然后大家会在ondestroy方法里面取消注册
  8. public void onDestroy() {
  9. super.onDestroy()
  10. EventBus.getDefault().unregister(this)
  11. }
  12. //使用LiveEventBus的话,就不需要有注册和取消注册这一步了,只需要在application里面初始化的时候,设置自动回收就可以了
  13. LiveEventBus.
  14. config().
  15. supportBroadcast(this). //是否支持使用广播发送数据,这个就可以跨进程通信啦
  16. lifecycleObserverAlwaysActive(true). //设置为true,则整个生命周期都能收到消息,为false,就只有激活状态才能收到,默认是true
  17. autoClear(true); //在没有Observer关联的时候是否自动清除LiveEvent以释放内存
  18. //发送方法
  19. LiveEventBus.get("colorEvent").post("test")
  20. //接收方法
  21. LiveEventBus.get("colorEvent").observe(this, new Observer<Object>() {
  22. @Override
  23. public void onChanged(Object o) {
  24. }
  25. });

那么换成kotlin是这样用的

  1. //发送数据
  2. LiveEventBus.get("colorEvent").post("test")
  3. //接收数据
  4. LiveEventBus.get("colorEvent").observe(this, Observer {
  5. //是不是有点没看懂?这样怎么接收数据呢,
  6. //别急,这里你会看到有个it参数,它就是值,而且是泛型的
  7. //比如我们这里发送的是一个string字符串,那么接收就可以这样写
  8. val test:String = it.toString()
  9. //而如果我们post的不是字符串,是一个实体类怎么办呢?
  10. //假设我们发送的是这样:
  11. //LiveEventBus.get("colorEvent").post(ColorEvent("#ffffff"))
  12. //这里ColorEvent的实体类我就不贴代码了,自行脑补
  13. //那么接收代码就是这样:
  14. val colorEvent:ColorEvent = it as ColorEvent
  15. //如果我们一个类里面有多个接收方法,那么就这样接收:
  16. if(it is ClassA){ //这里的 it is ClassA 就是java代码的 it instanceof ClassA
  17. }else if(it is ClassB){
  18. }
  19. })

4.Intent跳转Activity

java写法:

  1. Intent intent = new Intent(this,ClassA.class);
  2. startActivity(intnet)

kotlin写法:

  1. var intent = Intent(this,ClassA::class.java)
  2. startActivity(intent)

5. Handler

java写法:

  1. private Handler mHandler =new Handler(){
  2. @Override
  3. public void handleMessage(Message msg) {
  4. mHandler.sendEmptyMessageDelayed(0,1000);
  5. }
  6. };

kotlin写法:

  1. private val mHandler = object :Handler(){
  2. override fun handleMessage(msg: Message?) {
  3. //这里特别留意,因为java的写法是要用mHandler.出来的
  4. //但如果这里前面写个mHandler.那么会编译不通过
  5. sendEmptyMessageDelayed(0,1000)
  6. }
  7. }

6.jetpack---viewbinding

这绝对是我刚接触jetpack以来认为第一个吊炸天的东西,可以省略非常多的代码。基本再也不用findviewById,也不用BindView(butterknife的用法)了,试想一下:一个复杂的界面,几十个id需要处理,光写findview就花了几分钟,还占用大量篇幅

  1. //在模块的build.gradle文件下加入这行代码即可
  2. //但用这玩意,就意味着你要用kotlin来写代码,是的,google就是这么骚,为了让
  3. //用户转变为用kotlin来写代码,这玩意就只支持kotlin
  4. apply plugin: 'kotlin-android-extensions'
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <TextView
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:text="Hello World!"
  12. app:layout_constraintBottom_toBottomOf="parent"
  13. app:layout_constraintLeft_toLeftOf="parent"
  14. app:layout_constraintRight_toRightOf="parent"
  15. app:layout_constraintTop_toTopOf="parent"
  16. android:id="@+id/tv_hello"
  17. />
  18. </androidx.constraintlayout.widget.ConstraintLayout>
  1. //这行代码就是把view都引入啦,只要xml布局有id的,这里都有
  2. import kotlinx.android.synthetic.main.activity_main.*
  3. class KtMainActivity : AppCompatActivity(){
  4. override fun onCreate(savedInstanceState: Bundle?) {
  5. super.onCreate(savedInstanceState)
  6. setContentView(R.layout.activity_main)
  7. tv_hello.text = "aaaa" //这里直接使用id的名称就可以了
  8. }
  9. }

7. 类引用

java写法

startActivity(new Intent(this,SecondActivity.class))

kotlin写法

startActivity(Intent(this@HelloActivity,SecondActivity::class.java))

8.ExpandListView的convertView引用

java写法

  1. @Override
  2. public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
  3. if(convertView == null){
  4. convertView = LayoutInflater.from(context).inflate(R.layout.xxx,parent,false)
  5. }
  6. return convertView;
  7. }

kotlin写法

  1. override fun getGroupView(groupPosition: Int,isExpanded: Boolean,convertView: View?,parent: ViewGroup?): View {
  2. var convertView = convertView //这里必须重新赋值,直接用convertView的话,下面的convertView = xxx 就报错了,无法编译
  3. if(convertView == null) {
  4. convertView = LayoutInflater.from(mContext).inflate(R.layout.adapter_bfb_record_group,null)
  5. }
  6. return convertView!!
  7. }

9. jetpack -- 数据绑定(databinding)

个人认为,数据绑定这玩意的好处,是减少在activity中对于UI数据的操作,当activity比较复杂,上千行代码的时候,用databinding有助于代码简洁

  1. //它是jetpack的组件之一,需要在对应的运行模块的build.gradle文件中加入如下代码
  2. android{
  3. dataBinding {
  4. enabled = true
  5. }
  6. }
  1. //第二步,先写model层(个人习惯,你要先写xml布局也可以)
  2. //接口返回什么,就写什么咯
  3. data class User(val firstname:String)
  1. <!-- 第三步,写xml布局 -->
  2. <?xml version="1.0" encoding="utf-8"?>
  3. <layout xmlns:android="http://schemas.android.com/apk/res/android"
  4. xmlns:app="http://schemas.android.com/apk/res-auto"
  5. >
  6. <data>
  7. <!-- name自行定义,你想叫什么都行 -->
  8. <!-- type 就是对应的model层的类名-->
  9. <variable name="killaxiao" type="com.example.myapplication.User"/>
  10. </data>
  11. <androidx.constraintlayout.widget.ConstraintLayout
  12. android:layout_width="match_parent"
  13. android:layout_height="match_parent">
  14. <!-- @{killaxiao.firstname , default=helloWorld} 很好理解吧 -->
  15. <!-- @{}的语法,上面定义的name.model层的参数,default是默认值 -->
  16. <TextView
  17. android:layout_width="wrap_content"
  18. android:layout_height="wrap_content"
  19. android:text="@{killaxiao.firstname , default=helloWorld}"
  20. app:layout_constraintBottom_toBottomOf="parent"
  21. app:layout_constraintLeft_toLeftOf="parent"
  22. app:layout_constraintRight_toRightOf="parent"
  23. app:layout_constraintTop_toTopOf="parent"
  24. android:id="@+id/tv_hello"
  25. />
  26. </androidx.constraintlayout.widget.ConstraintLayout>
  27. </layout>
  1. override fun onCreate(savedInstanceState: Bundle?) {
  2. super.onCreate(savedInstanceState)
  3. //使用databinding之后,就不用使用setcontentview了
  4. //这里ActivityMainBinding 是因为我的布局文件叫activity_main,
  5. //如果你的布局文件叫activity_test,那么这里就是ActivityTestBinding
  6. val binding: ActivityMainBinding = DataBindingUtil.setContentView(
  7. this, R.layout.activity_main)
  8. //这里在实际使用中,就是从接口获取到值之后,设置进去即可
  9. binding.killaxiao = User("bbbbb")
  10. }

10.room数据库

这个数据库让我遇到的坑有点多,先说明基础用法

apply plugin: 'kotlin-kapt' //在运行模块的build.gradle目录加入代码
  1. dependencies{
  2. implementation "androidx.room:room-runtime:2.3.0"
  3. kapt "androidx.room:room-compiler:2.3.0" //我是用kotlin开发,所以前缀是kapt,如果是java的,请用annotationProcessor,但本文是介绍kotlin的,所以不做java的讲解
  4. }

引入写完了之后,第一步,先来创建一个表

  1. @Entity(tableName = "users") //这里如果不写tableName的话,那么表名就是类名(UserTable)
  2. data class UserTable(
  3. //下面这些应该不用过多解释了,userid主键自增,其他的name = "xxx"就是列名
  4. @PrimaryKey(autoGenerate = true) var userid:Int,
  5. @ColumnInfo(name = "username") var username:String,
  6. @ColumnInfo(name = "password") var password:String,
  7. @ColumnInfo(name = "permission") var permission:String,
  8. @ColumnInfo(name = "phone") var phone:String,
  9. @ColumnInfo(name = "company") var company:String
  10. )

然后写它的增删改查方法

  1. @Dao
  2. interface UserDao { //必须是接口
  3. @Query("select * from users where userid = :id") //方法中的参数,用:id来获取
  4. fun getUserById(id:Int):UserBean
  5. @Query("select * from users where username = :name")
  6. fun getUserByName(name:Int):UserBean
  7. @Query("select count(*) from users where username = :username and password = :password")
  8. fun Login(username:String,password:String):Int
  9. @Insert(onConflict = OnConflictStrategy.REPLACE)
  10. fun insertUser(user:UserTable)
  11. @Update
  12. fun updateUser(user:UserTable)
  13. @Query("update users set username = :username and password = :password and phone = :phone and company = :company where userid = :id")
  14. fun updateUserById(id:Int,username:String,password:String,phone:String,company:String)
  15. }

接下来创建数据库

  1. //entities里面,有几个表,就写几个创建表对应的类名,version版本号不用说,最后一个参数exportSchema可以不填,默认是true
  2. @Database(entities = [UserTable::class,MessageTable::class],version = 1,exportSchema = true)
  3. abstract class AppDatabase :RoomDatabase(){
  4. abstract fun userDao():UserDao
  5. abstract fun messageDao():MessageDao
  6. companion object{
  7. private var instance:AppDatabase? = null
  8. fun getInstance(context: Context):AppDatabase{
  9. if(instance == null){
  10. //allowMainThreadQueries是允许在主线程执行
  11. instance = Room.databaseBuilder(context.applicationContext,AppDatabase::class.java,"xingtong")
  12. .allowMainThreadQueries().build()
  13. }
  14. return instance as AppDatabase
  15. }
  16. }
  17. }
  1. //启动页copy了以前项目的,所以是java写的,大家可以自己写kotlin的代码
  2. AppDatabase database = AppDatabase.Companion.getInstance(WelcomeActivity.this);
  3. try {
  4. UserDao userDao = AppDatabase.Companion.getInstance(WelcomeActivity.this).userDao();
  5. //这里是自己写的工具类,读取assets目录下的default_user.json,代码就不贴了
  6. JSONObject jsonObject = new JSONObject(AssetsReader.getJson("default_user.json", WelcomeActivity.this));
  7. JSONArray jsonArray = jsonObject.optJSONArray("data");
  8. for(int i=0;i<jsonArray.length();i++){
  9. JSONObject obj = jsonArray.optJSONObject(i);
  10. UserTable userTable = new UserTable(-1,obj.optString("username"),
  11. obj.optString("password"),obj.optString("permission"),
  12. obj.optString("phone"),obj.optString("company"));
  13. userDao.insertUser(userTable);
  14. }
  15. startActivity(new Intent(WelcomeActivity.this, LoginActivity.class));
  16. finish();
  17. }catch (Exception e){
  18. e.printStackTrace();
  19. }

NOTE:接下来说坑

1.编译时报错

Schema export directory is not provided to the annotation processor so we cannot export the schema. You can either provide `room.schemaLocation` annotation processor argument OR set exportSchema to false.

这个要在刚才的运行模块build.gradle里面加入代码,或者创建数据库的代码那里 设置 exportSchema =false,不过推荐下面这种做法

  1. defaultConfig {
  2. applicationId "xxx"
  3. minSdkVersion 23
  4. targetSdkVersion 28
  5. versionCode 1
  6. versionName "1.0"
  7. testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  8. //加入以下代码
  9. javaCompileOptions {
  10. annotationProcessorOptions {
  11. arguments = ["room.schemaLocation":
  12. "$projectDir/schemas".toString()]
  13. }
  14. }
  15. }

2. 接收不到回调

  1. AppDatabase.Companion.getInstance(WelcomeActivity.this,new RoomDatabase.Callback(){
  2. @Override
  3. public void onCreate(@NonNull SupportSQLiteDatabase db) {
  4. try {
  5. UserDao userDao = AppDatabase.Companion.getInstance(WelcomeActivity.this).userDao();
  6. JSONObject jsonObject = new JSONObject(AssetsReader.getJson("default_user.json", WelcomeActivity.this));
  7. JSONArray jsonArray = jsonObject.optJSONArray("data");
  8. for(int i=0;i<jsonArray.length();i++){
  9. JSONObject obj = jsonArray.optJSONObject(i);
  10. UserTable userTable = new UserTable(-1,obj.optString("username"),
  11. obj.optString("password"),obj.optString("permission"),
  12. obj.optString("phone"),obj.optString("company"));
  13. userDao.insertUser(userTable);
  14. }
  15. startActivity(new Intent(WelcomeActivity.this, LoginActivity.class));
  16. finish();
  17. }catch (Exception e){
  18. e.printStackTrace();
  19. }
  20. }
  21. });

在创建数据库的时候,获取回调,是没有响应的,直到有对数据库的具体操作,才会有回调,所以这个回调有点鸡肋,大可不用

3. 上述代码引发的崩溃

java.lang.IllegalStateException: getDatabase called recursively

这是因为AppDatabase.Companion.getInstance得到了一个数据库的对象引用,然后我在getInstance这个方法里面写了

instance!!.beginTransaction() 这行代码,即可收到onCreate回调,然后在回调这里再获取引用,在userDao.insertUser的时候报错了,在获取getInstance的时候,数据库引用已经锁了

所以我去掉了getInstance方法里面的instanse!!.beginTransaction(),改成如下代码,即可正常运行

  1. AppDatabase database = AppDatabase.Companion.getInstance(WelcomeActivity.this);
  2. try {
  3. UserDao userDao = database.userDao();
  4. JSONObject jsonObject = new JSONObject(AssetsReader.getJson("default_user.json", WelcomeActivity.this));
  5. JSONArray jsonArray = jsonObject.optJSONArray("data");
  6. for(int i=0;i<jsonArray.length();i++){
  7. JSONObject obj = jsonArray.optJSONObject(i);
  8. UserTable userTable = new UserTable(-1,obj.optString("username"),
  9. obj.optString("password"),obj.optString("permission"),
  10. obj.optString("phone"),obj.optString("company"));
  11. userDao.insertUser(userTable);
  12. }
  13. startActivity(new Intent(WelcomeActivity.this, LoginActivity.class));
  14. finish();
  15. }catch (Exception e){
  16. e.printStackTrace();
  17. }

11.kotlin协程

协程的概念在kotlin1.3的版本提出来,所以如果要使用协程的话,依赖的版本要高于1.3才行。至于协程是啥?笔者担心解释不清楚,建议大家还是去看官方的描述,可以简单的认为,协程,就是可以让你用同步化的逻辑去执行异步的代码,可以替代一些回调函数。

笔者在写文章的时候,官方的协程版本是1.3.9

  1. //在项目执行模块的build.gradle配置文件中加入kotlin协程
  2. implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
  3. implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"

启动协程的几种方式:

1.runBlocking{},这种方式启动的协程会阻塞当前线程,直至runBlocking里面的逻辑执行结束,实际使用中,应避免使用该方式启动协程,常用于测试协程代码用

  1. var a = 0
  2. runBlocking {//创建一个协程,但这个协程是会阻塞当前线程的,直至协程的逻辑运行完毕
  3. a += 1
  4. }
  5. Log.e("test","a的值:$a") //因为runBlocking会阻塞线程,所以这里a输出的值是1

如果改成这样,那么程序就会卡死

  1. var a = 0
  2. runBlocking {//创建一个协程,但这个协程是会阻塞当前线程的,直至协程的逻辑运行完毕
  3. while (true) {
  4. a += 1
  5. Log.e("test","a的值:$a")
  6. }
  7. }

2.GlobalScope.launch{},这种方式启动的协程不会阻塞当前线程,但它的生命周期是等同于当前应用的生命周期,即假设这个协程在Test1Activity中启动,然后Test1Activity跳转至Test2Activity,并finish掉了Test1Activity,但协程内的代码依然还是在执行的,实际使用中,可以替代部分后台service执行的代码逻辑,而其它的情况则应该要避免使用这种方式启动协程

  1. GlobalScope.launch {//这里也是创建一个协程,但这个协程的生命周期是整个应用程序的生命周期,也就是说,即使这个activity销毁了,协程也还在执行
  2. while (true){ //因为这种启动方式不会阻塞线程,所以即使一直执行,也不会卡死界面
  3. test_result+=1
  4. Log.e("test","test_result的值:$test_result")
  5. }
  6. }
  7. btn_click.setOnClickListener {
  8. //点击之后跳转到Test2Activity,并finish掉当前activity,但因为上方协程生命周期是跟随应用程序的生命周期,不会在activity关闭后停止运行,所以即使到了Test2Activity,也还是一直在输入test_result的值
  9. startActivity(Intent(this,Test2Activity::class.java))
  10. finish()
  11. }

3.CoroutineScope + launch{}/async{},这种方式启动的协程不会阻塞线程,并且会跟随当前生命周期的结束而结束,所以实际使用中,应尽量使用这种方式启动协程

3.1 在activity中实现协程

  1. class MainActivity : BaseActivity(),CoroutineScope {
  2. override val coroutineContext: CoroutineContext
  3. get() = Dispatchers.Main //会阻塞主线程,官方描述是这种调度器可在主线程执行协程,只能用于界面交互和执行快速工作
  4. //get() = Dispatchers.IO //不会阻塞主线程,官方描述是用于读写文件,访问网络用的
  5. //get() = Dispatchers.Default //不会阻塞主线程,最大并发数是CPU的核心数,默认2,官方描述是用于占用大量CPU资源的工作,比如对列表排序或者解析json
  6. private lateinit var test1_job:Job
  7. private lateinit var test2_job:Job
  8. override fun initView() {
  9. test1_job=launch {
  10. while (true) {
  11. if(test1_job.isActive) { //如果不判断协程是否在取消状态,那么即使调用test1_job.cancel,协程也不会停止运行
  12. test_result += 1
  13. Log.e("test", "test_result的值:$test_result")
  14. }
  15. }
  16. }
  17. test2_job=launch {
  18. var async1 = async {
  19. delay(3000) //这里特意延迟3秒,再返回5 所以这里设想一下,是网络请求
  20. 5
  21. }
  22. var async2 = async {
  23. delay(5000) //这里延迟5秒,再返回10 ,如果需要同步上面网络请求的结果一起做UI更新的话,async就派上了用场
  24. 10
  25. }
  26. //为了展示async的用法,所以上面创建了两个async的协程,分别延迟3秒和5秒,但因为async会同步执行,await()会等待执行结果,所以这里是会延迟5秒之后,得到
  27. //async1和async2的值,才输出日志
  28. Log.e("test","async1+async2=${async1.await()+async2.await()}") //输出的结果是15
  29. }
  30. }
  31. override fun onDestroy() {
  32. super.onDestroy()
  33. test1_job.cancel() //不写取消方法,不会跟随activity的销毁而销毁
  34. test2_job.cancel()
  35. }
  36. }

3.2 直接创建一个协程作用域

  1. var scope =CoroutineScope(Dispatchers.IO)
  2. var test3_job = scope.launch {
  3. while (true) {
  4. test_result += 1
  5. Log.e("test", "test_result的值:$test_result")
  6. }
  7. }

12.创建方法

其实这个很简单,但为什么要写呢,是因为我在用了kotlin几个月之后,才留意到原来也可以这么写

  1. val sumLanda:(Int,Int)->Int = {x,y ->x+y} //这个是有返回参数的,和下方写法完全一样
  2. fun sumLanda2(x:Int,y:Int):Int{
  3. return x+y
  4. }
  5. //但编辑器上能看得出来两种写法调用的区别,调用sumLanda是和参数调用一样的,是紫色的,但是调用sumLanda2是白色的
  6. Log.e("test",sumLanda(2,6).toString())
  7. Log.e("test",sumLanda2(2,6).toString())
  8. //如果不需要返回参数,就直接这么写就OK了
  9. val sumLanda3:(x:Int,y:Int)->Unit = {it1,it2-> //有多个参数,这里必须声明,因为这种属于是lambda表达式
  10. Log.e("test",(it1+it2).toString())
  11. }
  12. val sumLanda4:(x:Int)->Unit ={ //只有一个参数,就可以不声明,默认是it
  13. Log.e("test",it.toString())
  14. }

13.类型检测

咱们写java的朋友都知道,有些时候我们写了一些父类,用的T类型,然后到具体的子类使用的时候需要进行类型检测,于是使用 instanceof 关键字,那么在kotlin里面使用的是 is 关键字,不过kotlin还提供了 !is 方法,就是除开某某类型的意思,看代码

  1. fun isTest(obj:Any):Int?{
  2. if(obj !is String){
  3. return obj.toString().length
  4. }
  5. return null
  6. }
  7. Log.e("test","isTest:"+isTest(3)) //输出isTest:1
  8. Log.e("test","isTest:"+isTest("12345")) //输出isTest:null
  9. fun isTest2(obj:Any):Int?{
  10. if(obj is String){
  11. return obj.toString().length
  12. }
  13. return null
  14. }
  15. Log.e("test","isTest2:"+isTest2(3)) //输出isTest2:null
  16. Log.e("test","isTest2:"+isTest2("12345")) //输出isTest2:5

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/474432
推荐阅读
相关标签
  

闽ICP备14008679号