Kotlin generic type inference limitation
Kotlin generic type inference doesn’t support super constructor call’s argument type.
Are you feel weird about generic type inference? Take a look at the Kotlin limitation.
The IDE says:
Type argument is not within its bounds.
A possible reason is Kotlin doesn’t inference the generic type from its parent.
The issue
Let’s see this example:
interface Cache<T> {
fun setValue(value: T?)
fun getValue(): T?
}
I declare a Cache
with setter and getter for any possible type.
interface Blobopen class BlobCache<T : Blob> : Cache<T> {
private var blob: T? = null
override fun setValue(value: T?) {
blob = value
}
override fun getValue(): T? {
return blob
}
}
Then, I declare a data type called Blob
and a BlobCache
for implementation of Cache
which temporarily saves cache in the memory.
To convenient use cache everywhere, I also write a manager like this:
abstract class BlobCacheManager<T : BlobCache<Blob>> {
private var cache: T? = null
fun init() {
cache = createCache()
}
fun save(blob: Blob) {
cache?.setValue(blob)
}
protected abstract fun createCache(): T
}
This BlobCacheManager
is a template for operating any cache of Blob
. A child class only required to implement the createCache()
.
The usage I want is a ImageCache
for saving Image
in the memory and a ImageCacheManager
for using easily. The code looks like below:
class Image(private val expiredAt: Long) : Blob {
fun isExpired(): Boolean {
return System.currentTimeMillis() > expiredAt
}
}
class ImageCache : BlobCache<Image>() {
override fun setValue(value: Image?) {
if (value == null || !value.isExpired()) {
super.setValue(value)
}
}
}// ImageCache: Type argument is not within its bounds.
class ImageCacheManager : BlobCacheManager<ImageCache>() {
override fun createCache(): ImageCache {
return ImageCache()
}
}
But when I declare ImageCache
in the supertype constructor of BlobCacheManager
, the IDE says Type argument is not within its bound.
Type argument is not within its bound.
Expected: BlobCache<Blob>
Found: ImageCache
It’s weird. ImageCache
is a child of BlobCache<Image>
we just declare it.
The cause
The cause is Kotlin inference generic type only by two kinds of type:
- Local type inference.
- Function signature type inference
You can find out more detail in this document.
Solution
Kotlin doesn’t infer the generic type from a supertype argument. Why don’t we just give it the actual generic type?
abstract class BlobCacheManager<D: Blob, T : BlobCache<D>> {
private var cache: T? = null
fun init() {
cache = createCache()
}
fun save(blob: D) {
cache?.setValue(blob)
}
protected abstract fun createCache(): T
}class ImageCacheManager : BlobCacheManager<Image, ImageCache>() {
override fun createCache(): ImageCache {
return ImageCache()
}
}
BlobCacheManager
requires its child class to pass the exact generic type D
and implement ImageCacheManager
with an explicit generic type Image
.
Finally, the build error has gone, and everything works fine. The solution is simple. We declare two arguments if we can’t do it by one supertype argument.