Reputation: 148169
Suppose I have a generic function:
fun <T: Foo> bar(t: T) { ... }
Then, later at some point in time, I decided that it is meaningless (or even erroneous) to call bar(...)
with T
specialized as Qux
, which is, of course, one of the Foo
's subtypes. But I have a strong reason not to change the classes hierarchy (e.g. I don't have access to that code).
Is there a way to prohibit the calls to bar(t)
with T
specialized as Qux
?
Upvotes: 1
Views: 89
Reputation: 148169
There is a way to prohibit the calls for a certain generic type argument: the deprecation. Using the @Deprecated
annotation in this case looks quite clumsy, but it solves the problem (*).
You can define an overload of bar(...)
with a more concrete type and deprecate the overload with level DeprecationLevel.ERROR
:
@Deprecated("bar should not be called with Qux", level = DeprecationLevel.ERROR)
fun <T : Qux> bar(t: T) = bar(t as Foo)
After that, when a bar(...)
call is resolved for a Qux
argument, the deprecated overload will take precedence because it features more concrete types. Therefore the calls will produce a compile-time error:
bar(Qux())
^ Using 'bar(T): Unit' is an error. bar should not be called with Qux
(*) Note, however, that this will only work for Qux
as static type (the one used for the call resolution), and the function can still be called as bar(Qux() as Foo)
. Quite expectedly, you cannot produce a compile-time error if the argument type is only known at runtime.
The same approach can be used to produce errors or warnings for functions that should only react on null
s in some way, so that calling them with a not-null type makes no sense:
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?)
Upvotes: 3