如何调用从 Kotlin 获取非空 Void 参数的 Java 方法?

Posted

技术标签:

【中文标题】如何调用从 Kotlin 获取非空 Void 参数的 Java 方法?【英文标题】:How do I call a Java method that takes a Non-null Void parameter from Kotlin? 【发布时间】:2022-01-17 10:02:34 【问题描述】:

主要问题

是否可以从 Kotlin 调用具有这样签名的 Java 方法(来自第三方库,因此无法更改方法签名)?如果有,怎么做?

class Uncallable 
    void myMethod(@NonNull Void a) 
        System.out.println("Ran myMethod");
    

我可以从另一个 Java 类调用它

Uncallable u = new Uncallable();
u.myMethod(null); // IDE warns about nullability but still runs fine

但在 Kotlin 中,这些选项都不起作用

val u = Uncallable()
u.myMethod() // No value passed for parameter 'a'
u.myMethod(null) // Null can not be a value of a non-null type Void
u.myMethod(Unit as Void) // ClassCastException: class kotlin.Unit cannot be cast to class java.lang.Void
u.myMethod(Void)
u.myMethod(Unit)
u.myMethod(Nothing)

类似的问题(如this one)建议创建一个Java 接口层来让我绕过可空性规则。这在这里有效,但我想知道是否有任何方法可以在没有这样一个额外层的情况下调用它。我实际上不需要传递空值,只需调用方法即可。

This question 解决了类似的问题,但他们可以控制界面并且可以使用 Void? 作为类型。

我为什么要这样做?

我不是真的...但是,我有一些单元测试可以拦截添加到某些 Firebase 方法的 addOnSuccessListener 回调,并在提供的侦听器上调用 onSuccess。该回调的签名使用Void 作为其参数类型,之前我可以调用listener.onSuccess(null)

doAnswer  invocation ->
    val args = invocation.arguments
    @Suppress("UNCHECKED_CAST")
    val l = args[0] as OnSuccessListener<Void>
    l.onSuccess(null)
    mMockTask
.`when`(mMockTask).addOnSuccessListener(ArgumentMatchers.any())

在最近更新 Firebase 库后,似乎 @NonNull 注释已添加到 onSuccess 的参数中,或者 Kotlin 更改为开始强制执行它

public interface OnSuccessListener<TResult> 
    void onSuccess(@NonNull TResult var1);

因此,我无法再调用 listener.onSuccess(null) - 我收到有关为非空参数传递空值的构建错误。

我可以创建一个VoidCaller Java 接口来解决这个问题,

public class VoidCaller 
    static void callSuccessCallback(OnSuccessListener<Void> callback) 
        callback.onSuccess(null);
    

    static void callFailureCallback(OnFailureListener callback) 
        callback.onFailure(null);
    

这可行,但前提是我将回调从

fileRef.delete().addOnSuccessListener  onSuccess() 

fileRef.delete().addOnSuccessListener  _ : Void? -> onSuccess() 

或者我尝试将非 null 类型的 it 设置为 null 时出错。

Parameter specified as non-null is null: method com.app.firebaseHelper.deleteImage$lambda-11$lambda-10, parameter it

理想情况下,我想找到一种直接从 Kotlin 调用此类方法的方法。

【问题讨论】:

如果你写null as Void会发生什么? (也就是说,根据定义,该方法签名应该是不可调用的。) 演员阵容失败,给我null cannot be cast to non-null type java.lang.Void 它添加了一个注解强制不传递空值,但是你尝试给它null,这很奇怪,我想如果你可以调用,但它会导致一些错误。除了注解没有在函数中使用。 @Lenoarod 我传递了 null 因为我无法实例化 Void 对象,现在 @NonNull 阻止了这种情况,我正在寻找一种调用该方法的方法 【参考方案1】:

如果你只想传递 Void 对象,但它的构造函数是私有的,你可以使用反射。

Constructor<Void> c = Void.class.getDeclaredConstructor();
c.setAccessible(true);
Void v = c.newInstance();

那么你就可以通过了。

由于问题是关于如何从 Kotlin 执行此操作,因此这里是 Kotlin 版本

fun makeVoid(): Void 
    val c: Constructor<Void> = Void::class.java.getDeclaredConstructor()
    c.isAccessible = true
    return c.newInstance()

val u = Uncallable()
u.myMethod(makeVoid())

【讨论】:

以上是关于如何调用从 Kotlin 获取非空 Void 参数的 Java 方法?的主要内容,如果未能解决你的问题,请参考以下文章

在 Kotlin 中测试非空参数

如何在 Kotlin 中获取泛型参数类

java.lang.IllegalArgumentException:指定为非空的参数为空:方法 kotlin.jvm.internal.Intrinsics.checkParameterIsNotN

如何在 Kotlin 的 JS 接口中使用可选参数调用

如何从片段 KOTLIN 中调用意图 [重复]

如何从 C# 调用具有 void* 回调和对象参数的 C++ Dll 中的函数