如何使用参数类型的某个子类型禁止函数调用?

时间:2017-02-16 18:50:04

标签: generics compiler-errors kotlin

假设我有一个通用函数:

fun <T: Foo> bar(t: T) { ... }

然后,在某个时间点,我决定用bar(...)专门作为T来调用Qux毫无意义(甚至是错误的),当然, Foo的一个子类型。但我有充分的理由不改变类层次结构(例如我无法访问该代码)。

有没有办法禁止拨打bar(t) T专门用作Qux的电话?

1 个答案:

答案 0 :(得分:3)

有一种方法可以禁止调用某个泛型类型参数:弃用。在这种情况下使用@Deprecated注释看起来非常笨拙,但它解决了问题(*)。

您可以使用更具体的类型定义bar(...)的重载,并使用级别DeprecationLevel.ERROR弃用重载:

@Deprecated("bar should not be called with Qux", level = DeprecationLevel.ERROR)
fun <T : Qux> bar(t: T) = bar(t as Foo)

之后,当为bar(...)参数解析Qux调用时,不推荐使用的重载将优先,因为它具有更多具体类型。因此调用将产生编译时错误:

bar(Qux())
^ Using 'bar(T): Unit' is an error. bar should not be called with Qux

(*)但请注意,这仅适用于Qux作为静态类型(用于呼叫解析的那个),并且该功能仍然可以称为bar(Qux() as Foo)。如果参数类型仅在运行时已知,则不能产生编译时错误。

可以使用相同的方法为函数生成错误或警告,这些函数只应以某种方式对null作出反应,因此使用非null类型调用它们是没有意义的:

fun checkNotNull(x: Any?) = x ?: throw IllegalStateException("Should not be null")

@Deprecated("It is meaningless with a not-null argument.", DeprecationLevel.WARNING)
@JvmName("checkNotNull--notNullArgument")
fun checkNotNull(x: Any) = checkNotNull(x as Any?)