简体   繁体   English

Kotlin:将函数作为参数传递得到类型不匹配

[英]Kotlin: passing function as argument got Type mismatch

While migrating my java Android app to Kotlin I've come across with the next issue:在将我的 java Android 应用程序迁移到 Kotlin 时,我遇到了下一个问题:

I have a generic class used to run background tasks as follows:我有一个用于运行后台任务的通用类,如下所示:

class TaskRunner {
    private val executor: Executor =
        Executors.newSingleThreadExecutor() 
    private val handler = Handler(Looper.getMainLooper())

    interface Callback<R> {
        fun onComplete(result: R)
    }

    fun <R> executeAsync(callable: Callable<R>, callback: Callback<R>) {
        executor.execute {
            val result: R
            try {
                result = callable.call()
                handler.post { callback.onComplete(result) }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

And then I'm doing a TaskRunner call as follows (have to say it was working fine in java):然后我正在执行如下的 TaskRunner 调用(不得不说它在 java 中运行良好):

override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
    val appSettings = instance!!
    val outputPath = appSettings.outputPathCache
    //
    val taskRunner = TaskRunner()
    taskRunner.executeAsync(
        ScreenshotSaver(
            arrayBitmap,
            outputPath!!,
            ""
        )
    ) { ipr: ImageProcessingResult -> this::onImageProcessingFinished(ipr) }
}

This is the interface:这是界面:

interface IImageListeners {
    fun onImageProcessingFinished(ipr: ImageProcessingResult)
}

And the method:和方法:

@Override
private fun onImageProcessingFinished(ipr: ImageProcessingResult) {
    val uris = ArrayList<Uri?>()
    for (file in ipr.screenShotFiles) {
        val uri = FileProvider.getUriForFile(this, "$packageName.GenericFileProvider", file)
        if (uri!=null) uris.add(uri)
    }
    AWDrawerMenu.listener = this
    AWDrawerMenu.activity = WeakReference(this)
    AWDrawerMenu.shareFile(uris, "image/jpg")
}

Previously, in my first attempt, I was having a "Type Mismatch" compilation error, and now, after some modifications (like ::) the error is: Overload resolution ambiguity.以前,在我的第一次尝试中,我遇到了“类型不匹配”编译错误,现在,经过一些修改(如 ::),错误是:重载分辨率歧义。 All these functions match.所有这些功能都匹配。

public abstract fun onImageProcessingFinished(ipr: com.xxx.xxx.activities.main.ImageProcessingResult): Unit defined in com.xxx.xxx.activities.shared.BaseActivity

private final fun onImageProcessingFinished(ipr: com.xxx.xxx.activities.thought.result.ImageProcessingResult): Unit defined in com.xxx.xxx.activities.shared.BaseActivity

I'm stuck, and I don't know hot to pass a function as argument to a method in Kotlin.我被卡住了,我不知道如何将函数作为参数传递给 Kotlin 中的方法。

Edit 1:编辑1:

The only abstract classic my solution:唯一的抽象经典我的解决方案:

abstract class BaseActivity : AppCompatActivity(), ICommonActivityMethods, IActionListeners,
    IImageListeners, IWaitingDialog {
    var appSettings: AppSettings? = null
    var dialog: AlertDialog? = null
    var sharedPreferences: SharedPreferences? = null
    var screen: AWScreen? = null
    private var noInternetDialog: AWDialog? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE)
        setContext(this)
        setLocale()
        setDrawer()
        if (className() == "DisplayThoughtActivity") {
            checkInternetConnection()
        } else {
            startActivity()
        }
    }

    private fun className(): String {
        return this.javaClass.simpleName
    }

    private fun setDrawer() {
        setDrawer(this)
        setupDrawer(this)
    }

    private fun checkInternetConnection() {
        if (!AWHttp.isOnline()) {
            showNoInternetDialog()
        } else {
            startActivity()
        }
    }

    private fun showNoInternetDialog() {
        val title = AWLocale.getStringResourceByName("requestpermission_title")
        val message = AWLocale.getStringResourceByName("activity_displaythought_notonline")
        val positiveButtonText = AWLocale.getStringResourceByName("customalert_accept")
        val negativeButtonText = AWLocale.getStringResourceByName("customalert_cancel")
        if (noInternetDialog == null) {
            noInternetDialog = AWDialog(this)
            val rAccept = Runnable { checkInternetConnection() }
            val rCancel = Runnable {
                noInternetDialog!!.dismiss()
                startActivity()
            }
            noInternetDialog!!.title = title
            noInternetDialog!!.message = message
            noInternetDialog!!.closeOnAccept = true
            noInternetDialog!!.textColor = Color.parseColor("#ffffff")
            noInternetDialog!!.positiveButtonText = positiveButtonText
            noInternetDialog!!.positiveRunnable = rAccept
            noInternetDialog!!.negativeButtonText = negativeButtonText
            noInternetDialog!!.negativeRunnable = rCancel
            noInternetDialog!!.dismiss = false
            noInternetDialog!!.createYesNoDialog()
        }
    }

    override fun onBackPressed() {
        super.onBackPressed()
        AWAppearance.doTransitionOut(this)
    }

    override fun onResume() {
        super.onResume()
        setContext(this)
    }

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(updateBaseContextLocale(base))
    }

    override fun setLocale() {
        val currLang = AWLocale.locale
        AWLocale.locale = currLang
    }

    override fun startActivity() {
        if (dialog != null) dialog!!.dismiss()
        if (noInternetDialog != null) noInternetDialog!!.dismiss()
        AWDrawerMenu.listener = this
        setActivityLayout()
    }

    override fun setActivityLayout() {
        setContentView()
        setClassVariables()
        setActivityBackground()
        createActionBar()
        resizeActionBarBackButton(this)
    }

    override fun setClassVariables() {
        setContext(this)
        appSettings = instance
        dialog = AWDialog.createSpotsWaitDialog(this, R.style.CustomSpotsDialogLight)
        appSettings!!.dialog = dialog
        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        screen = AWScreen(this)
    }

    private fun updateBaseContextLocale(context: Context): Context {
        val language = AWLocale.locale
        val locale = Locale(language)
        Locale.setDefault(locale)
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            updateResourcesLocale(context, locale)
        } else updateResourcesLocaleLegacy(context, locale)
    }

    fun hideKeyboard(editText: EditText) {
        val imm = this.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
        Objects.requireNonNull(imm).hideSoftInputFromWindow(
            editText.windowToken,
            InputMethodManager.RESULT_UNCHANGED_SHOWN
        )
    }

    @TargetApi(Build.VERSION_CODES.N)
    private fun updateResourcesLocale(context: Context, locale: Locale): Context {
        val configuration = context.resources.configuration
        configuration.setLocale(locale)
        return context.createConfigurationContext(configuration)
    }

    private fun updateResourcesLocaleLegacy(context: Context, locale: Locale): Context {
        val resources = context.resources
        val configuration = resources.configuration
        configuration.locale = locale
        resources.updateConfiguration(configuration, resources.displayMetrics)
        return context
    }

    override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
        val appSettings = instance!!
        val outputPath = appSettings.outputPathCache
        //
        val taskRunner = TaskRunner()
        taskRunner.executeAsync(
            ScreenshotSaver(
                arrayBitmap,
                outputPath!!,
                ""
            )
        ) { ipr: ImageProcessingResult -> this::onImageProcessingFinished(ipr) }
    }

    @Override
    private fun onImageProcessingFinished(ipr: ImageProcessingResult) {
        val uris = ArrayList<Uri?>()
        for (file in ipr.screenShotFiles) {
            val uri = FileProvider.getUriForFile(this, "$packageName.GenericFileProvider", file)
            if (uri!=null) uris.add(uri)
        }
        AWDrawerMenu.listener = this
        AWDrawerMenu.activity = WeakReference(this)
        AWDrawerMenu.shareFile(uris, "image/jpg")
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
    }

    override fun showWorkingDialog(message: String?) {
        WaitingDialog.context = WeakReference(this)
        WaitingDialog.setInstance(message)
    }

    override fun clearWorkingDialog() {
        if (WaitingDialog.instance != null) {
            WaitingDialog.instance!!.dismiss()
        }
    }

    companion object {
        @JvmStatic
        fun resizeActionBarBackButton(activity: Activity) {
            val tvTitle = activity.findViewById<AutoResizeTextView>(R.id.tvActionBarTitle)
            val treeObserver = tvTitle.viewTreeObserver
            treeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    tvTitle.viewTreeObserver
                        .removeOnGlobalLayoutListener(this)
                    val newImgBackHeight = tvTitle.height
                    val imgBack = activity.findViewById<ImageView>(R.id.imgBack)
                    val treeObserverImgBack = imgBack.viewTreeObserver
                    treeObserverImgBack.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
                        override fun onGlobalLayout() {
                            imgBack.viewTreeObserver
                                .removeOnGlobalLayoutListener(this)
                            val reduction = AWScreen.dp2px(10)
                            imgBack.layoutParams.height = newImgBackHeight - reduction
                            imgBack.requestLayout()
                        }
                    })
                }
            })
        }
    }
}

Edit 2:编辑2:

I changed the problematic fun to:我将有问题的乐趣更改为:

override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
    val appSettings = instance!!
    val outputPath = appSettings.outputPathCache
    //
    val taskRunner = TaskRunner()
    taskRunner.executeAsync(
        ScreenshotSaver(
            arrayBitmap,
            outputPath!!,
            ""
        )
    ) { ipr: String -> (this::onImageProcessingFinished)(ipr) }
}

在此处输入图像描述

It's described in error message.它在错误消息中描述。 Based on your code base, you should pass a callback as the second attribute to executeAsync method because you are using callback pattern.根据您的代码库,您应该将回调作为第二个属性传递给 executeAsync 方法,因为您使用的是回调模式。 Otherwise you can use inline function instead of it if you change your executeAsync method signature and pass a lambda function instead of callback.否则,如果您更改 executeAsync 方法签名并传递 lambda 函数而不是回调,则可以使用内联函数代替它。 So please follow one of the below approaches:因此,请遵循以下方法之一:

1. 1.

fun <R> executeAsync(callable:  
 Callable<R>, callback: Callback<R>) {
   //Do what you want
   callback.onComplete(result)
}

And while you want to call it from outside of the class当你想从课堂外调用它时

taskRunner.executeAsync(
    ScreenshotSaver(
        arrayBitmap,
        outputPath!!,
        ""
    ),
    object: Callback<R> {
      override fun onComplete(result: 
       R) {
        //Do what you want with result
      }
    }
)
fun <R> executeAsync(callable: Callable<R>, callback: (String)->Unit) {
   //Do what you want
   callback.invoke(result)
}

And while you want to call it from outside of the class当你想从课堂外调用它时

taskRunner.executeAsync(
    ScreenshotSaver(
        arrayBitmap,
        outputPath!!,
        ""
    ) ) {result -> 
      //Do what you want with result
    }

Apparently I've found the issue, that's why I'll answer my own question.显然我已经找到了这个问题,这就是为什么我会回答我自己的问题。 First of all below there is the correct syntax (at least no more compilation problems).首先下面有正确的语法(至少没有更多的编译问题)。

I suppose ScreenshotSaver was returning a different object type than the expected by onImageProcessingFinished.我想 ScreenshotSaver 返回的对象类型与 onImageProcessingFinished 预期的对象类型不同。

I had two different ImageProcessingResult result classes in different packages, and now set only one for all.我在不同的包中有两个不同的 ImageProcessingResult 结果类,现在只为所有设置一个。

Still need to test it, but I guess this was the key.仍然需要测试它,但我想这是关键。

override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
    val appSettings = instance!!
    val outputPath = appSettings.outputPathCache
    val taskRunner = TaskRunner()
    assert(outputPath != null)
    taskRunner.executeAsync(
        ScreenshotSaver(arrayBitmap, outputPath!!, null)
    ) { ipr -> onImageProcessingFinished(ipr) }
}

override fun onImageProcessingFinished(ipr: ImageProcessingResult) {
    val uris = ArrayList<Uri?>()
    for (file in ipr.screenShotFiles) {
        val uri = FileProvider.getUriForFile(this, "$packageName.GenericFileProvider", file)
        if (uri!=null) uris.add(uri)
    }
    AWDrawerMenu.listener = this
    AWDrawerMenu.activity = WeakReference(this)
    AWDrawerMenu.shareFile(uris, "image/jpg")
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM