Reputation: 70
Given a class:
class Data<T>{
fun get(): T = Something as T
fun update(item: T) { }
}
How to do something like this?
fun alterData(d: Data<*>){
d.update(d.get())
}
The function "alterData" won't compile:
Out-projected type 'Data<*>' prohibits the use of 'fun update(item: T)'
Update: The caller of alterData() don't know the type, so this is not possible:
fun <T>alterData(d: Data<T>){
d.update(d.get())
}
The only way I found to bypass this limitation is by means of writing the method inside the class, but that's breaks abstraction
Upvotes: 0
Views: 124
Reputation: 4676
*
basically means that you don't know what the type T
is in this case, so it could be anything.
Because you have defined d
as Data<*>
the call d.get()
returns Any?
. The compiler cannot know what type of data d
contains. Therefor you have to expect anything that's valid for T
, including null
.
Because you have defined d
as Data<*>
the call d.update(…)
accepts only Nothing
, i.e. no value at all. The compiler cannot know what type of data d
is allowed to receive. Therefor it cannot accept anything as it may or may not be valid here.
What you want to tell the compiler is that there is a relationship between what comes out of d
(out projection) and what goes into d
(in projection):
fun <T> alterData(d: Data<T>) {
d.update(d.get())
}
Now the compiler knows that whatever comes out of d
(through .get()
) is of the same type as what you put into d
(through .update(…)
). In both cases that type is referred to as T
.
Note that the "type parameter" T
doesn't have to have same name as your Data<T>
declaration. The following is equally valid to indicate the relationship:
fun <Something> alterData(d: Data<Something>) {
d.update(d.get())
}
It may be easier to understand if you break the code down further:
fun alterData(d: Data<*>) {
// `value` is of type `Any?` because we don't know `T` of `Data`
val value = d.get()
// `update` rejects `Any?` b/c we don't know `T` and it may not be valid
d.update(value)
}
fun <Something> alterData(d: Data<Something>) {
// `value` is of type `Something` b/c we know that `T` of `Data` is `Something`
val value = d.get()
// `update` accepts `Something` b/c we know `T` is `Something`
d.update(value)
}
As in your case neither the caller nor the callee (alterData
) know the actual type of T
you"ll have to tell the compiler to ignore compile-time type safety by using a cast.
You can do that either in the caller or the callee. Where it makes more sense depends on the actual use case. Usually the caller knows a little more about the context and the possible type of T
than the callee, so it makes sense to perform a cast there.
fun foo(d: Data<*>) {
// ignore all type safety for `T`
alterData(d as Data<Any?>)
}
The callee alterData
immediately passes a value that it receives from one Data
instance (d
) back to the same instance. In that case it's safe to assume that that can never go wrong, given your definition of class Data
. If that's the case then you can safely perform the case in the alterData
function:
fun alterData(d: Data<*>){
// ignore type safety and allow any value to be passed into `d`
(d as Data<Any?>).update(d.get())
}
Both approaches will raise a compiler warning that you could ignore using @Suppress("UNCHECKED_CAST")
.
Upvotes: 3