How can I specify a generic type parameter which i...
# getting-started
m
How can I specify a generic type parameter which is either the same or the nullable version of another type parameter? I have something like
<T : Any, PossiblyNullableT : T?>
, where T can be any type, and
PossiblyNullableT
has to be the same type or the nullable version of it. Basically the two possible ways to call my function would be:
func<String, String>()
func<String, String?>()
But I don't want to allow subclasses of
T
as the second type parameter, it either has to be the same type, or the nullable version.
j
Could you please share the specific use case?
e
do you really need this? if you have a single type parameter
<T>
it can be either nullable or non-nullable, and you can use the type
T & Any
when the non-nullable version is strictly required
m
My function returns a value of some type, or a default if the value is missing. The value it returns is not nullable, but the caller provided default can be null, if the caller wishes so. The return value is then either nullable or not depending on type of the default parameter.
fun getValue<Type, Default>(default: Default): Default = { return [..] ?: default }
I need to constrain the
Default
type so that it's the same as
Type
, or the nullable version.
e
why do you need that?
j
The value it returns is not nullable
That doesn't seem to be the case, otherwise what's the point in using
?:
?
m
The underlying source of the data is nullable, but in case the underlying source is null, I want to return the caller provided default value. Which could be null, but doesn't have to be.
e
Cf. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/if-empty.html doesn't constrain the type of the default fallback, which makes it more useful
m
Thanks, looking at the definition of
ifEmpty
and the fact constraining the type of the default is not actually necessary for me helped. All I need is
fun <T : Default, Default> getValue(default: Default): Default = readData<T>() ?: default
.
Actually that doesn't seem to work when I go a bit up my call chain. Because now I can't specify that
Default
has to be a supertype of another type. Minimal example:
e
if you really need that,
Copy code
@Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER")
fun <T, Default> getStringValue(default: Default): Default where T: String, T: Default = getValue(default)
but note that it breaks Java interop. but why would you need that?
m
That does seems to work. For why I need that, I probably don't, just can't think of another solution right now. Here is what I need: I have a
getStringValue
function that returns a String. I just want to add a default argument, so the caller can specify a default in case the function can't return the string it should. The default could be null. Simplest solution would be to just add a
default: String?
argument, but that changes my return type to
String?
. I don't want the return type to become nullable when it's called with
getStringValue("default")
, since it will never actually return null. At that point in attempt to solve that, I made the default type generic, so that if the caller specifies a non-nullable default, the return type will be kept non-nullable. But this resulted in the problem that it's now possible to specify a default type different than
String
or
String?
, which makes it not compile: now I can't
return [String?] ?: [Default]
, because String could possibly not be a subclass of Default, arriving at my original question: How can I constraint the
Default
type to be either
String
or
String?
. (Or in the general case, to be either
T
or
T?
)
In other words, I want a function that returns
String
when you provide a
String
argument, or returns
String?
when you provide
String?
argument. But the function normally returns a
String
, not just whatever is passed as the argument, so I can't make it accept arbitrary types and make them the return type, because then I can't return the normal
String
.
s
but the type checking is independent of parameters, so the returntype of your function is at compiletime indeed
String?
you could simply write and use two different functions:
fun getStringValueOr(default: String): String
and
fun getStringValueOrNull(): String?
. (of course
default
could have a default value
default: String = ""
)