在 Kotlin 中可以进行相交铸造吗?

Is intersection casting possible in Kotlin?

我在 Java 中有一个方法,如下所示:

public <T extends A & B> methodName(T arg, ...)

其中 A 是一个 class,B 是一个接口。

在我的 kotlin class 中,我有另一个 variable 类型的 C,我希望实现以下目标:

if (variable is A && variable is B) {
    methodName(variable, ...)
} else {
    // do something else
}

是否可以正确转换 variable 以便它可以用作参数而不会出错?

Currently, the variable has a setter method, so smart casting isn't available. However, I have also tested it with a local val and the value is inferred to have type Any which doesn't help.

Kotlin 不支持交集类型。这导致 variable 被智能转换为 Any,因为那是 AB 的共同祖先。

但是,Kotlin 确实支持泛型类型约束。您可以使用它来将类型参数限制为一种或多种类型。这可以用于方法和 classes。这是函数的语法(相当于 Kotlin 中的 methodName):

fun <T> methodName(arg: T)
    where T : A,
          T : B {
    ....
}

您可以通过创建扩展 AB 的 class 来解决您的问题,然后将这些类型的实现委托给您的对象。像这样:

class AandB<T>(val t: T) : A by t, B by t
    where T : A,
          T : B

您现在可以通过更改 if-test 来调用 methodName 来检查它是否是 AandB<*>:

if (variable is AandB<*>) {
    methodName(variable, ...)
}

你确实需要在某处将 variable 包装在 AandB 中。如果您没有任何地方可用的 variable 的类型信息,我认为您无法做到这一点。

注:AandB class 未实现 hashCodeequalstoString。您可以实现它们以委托给 t 的实现。

注意 2:这仅在 AB 是接口时有效。您不能委托给 class.

正如@marstran 指出的那样,when 子句是您指定多个边界的方式。这是关于 upper-bounds 的文档的 link。值得一提的是,如果您的边界之一是泛型类型参数,则您不能有多个边界。

您提到您尝试使用智能投射进行测试:

However, I have also tested it with a local val and the value is inferred to have type Any which doesn't help.

当前版本的 Kotlin (v1.4) 不是这种情况。您 不需要 创建一个 AandB class,因为您可以使用 val 或本地(捕获的)var 来智能-投向十字路口。

这是一个示例(和 runnable version):

interface I1 { fun one() = println("one") }
interface I2 { fun two() = println("two") }
class Both: I1, I2

val variable: Any = Both() // Starting with type 'Any'

if (variable is I1 && variable is I2) {
    // Type is now '(I1 & I2)' Smart-cast from Any
    variable.one()
    variable.two()
}

这里有一个 link 从 v1.4 开始对 Kotlin 交集类型进行更多讨论和可运行的示例