Change return type to 'T' errror - kotlin

I override this function from ViewModelProvider.Factory but I get an error with the generic return type T. Why?
class NewsViewModelProviderFactory(
val newsRepository: NewsRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return NewsViewModel(newsRepository) as T
}
}

It looks like the issue comes from the supertype of your type parameter T—that is, ViewModel?. According to the docs, create is declared in ViewModelProvider.Factory as
open fun <T : ViewModel> create(modelClass: Class<T>): T
Notice that it says <T : ViewModel>, not <T : ViewModel?>. This means that any consumer of a ViewModelProvider.Factory expects the create function to return a non-null ViewModel. However, the create function you've written could return null.
If you changed your function to
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return NewsViewModel(newsRepository) as T
}
that should alleviate the issue.

Related

How to write generic functions in Kotlin interfaces's implementations

I am trying to implement a generic HttpClient like this one:
interface HttpClient {
fun <T: Any> get(url: String): T?
}
implemented by a class like this:
class HttpClientImpl #Inject constructor(...) : HttpClient {
override fun <T : Any> get(url: String): T? = execute(url)
private inline fun <reified T: Any> execute(url: String): T? {
val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use {
return it.body?.parseBodySuccess()
}
}
private inline fun <reified T: Any> ResponseBody?.parseBody(): T? {
val type = objectMapper.typeFactory.constructType(T::class.java)
return this?.let { objectMapper.readValue(it.string(), type) }
}
}
Now, I would like to be able to call such GET method in this way:
data class MyEntity(...)
class MyService #Inject constructor(private val client: HttpClient) {
fun performGet(url: String): MyEntity? = client.get<MyEntity>(url)
}
However this is not allowed and the compiler throws an error referring to the line of code
override fun <T : Any> get(endpoint: String): T? = execute(endpoint)
flagging that : Cannot use 'T' as reified type parameter. Use a class instead.
I have been trying to re-write the line as
override inline fun <reified T : Any> get(endpoint: String): T? = execute(endpoint)
however, despite having to make the other two inline functions "non private" the compiler still won't compile because in this last way of writing the overriding function, it says:
Override by a function with reified type parameter
How can I achieve such generic function?
I ended up doing something like this:
interface HttpClient {
fun <T: Any> get(url: String, type: Class<T>): T?
}
implemented as:
class HttpClientImpl #Inject constructor(...) : HttpClient {
override fun <T : Any> get(url: String, type: Class<T>): T? = execute(url, type)
private fun <T: Any> execute(url: String, type: Class<T>): T? {
val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use {
return it.body?.parseBody(type)
}
}
private fun <T: Any> ResponseBody?.parseBody(type: Class<T>): T? {
val dataType = objectMapper.typeFactory.constructType(type)
return this?.let { objectMapper.readValue(it.string(), dataType) }
}
}
that I can call in this way:
data class MyEntity(...)
class MyService #Inject constructor(private val client: HttpClient) {
fun performGet(url: String): MyEntity? = client.get(url, MyEntity::class.java)
}
I would have preferred to pass the Type directly as an actual type like
client.get<MyEntity>(url)
rather than passing the Class as a parameter, however, just for now it works...
If anyone can suggest a better way of doing this, please let me know.
Updated
As suggested by Pawel, I have created an extra inline extension function to the HttpClient interface
inline fun <reified T:Any> HttpClient.get (url: String) = get(url, T::class.java)
And I'm now able to call the function the way I wanted.

I'm getting this error "com.example.viewmodelfactory.MainModelFactory cannot be cast to androidx.lifecycle.ViewModel "

Anyone tell me how to pass a default value in ViewModelFactory from MainActivity. I'm getting error com.example.viewmodelfactory.MainModelFactory cannot be cast to androidx.lifecycle.ViewModel
class MainModelFactory (val counter: Int): ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainModelFactory(counter) as T
}
This is my ViewModelClass
class MainViewModel(val intiallizeCount: Int) :ViewModel () {
var count : Int = intiallizeCount
fun Increment()
{
count++
}
}
I'm passing the default count value in MainActivity
mainViewModel=ViewModelProvider(this, MainModelFactory(10)).get(MainViewModel:: class.java)
ViewModelProvider.Factory returns as a ViewModel object i.e.
return MainViewModel(counter) as T

How to create an instance of a class by passing the type

I want to be able to say make an instance of this class and give a type then the code can instantiate a new instance of that class.
fun maker(type: Class<Animal>): Animal {
if(type == Class<Dog>) {
return Dog()
}
else if (type == Class<Cat>) {}
...
}
What is a good way to do this?
If they all have zero-argument constructors, you can do:
fun maker(type: Class<Animal>): Animal {
return type.newInstance()
}
You can make it return the type that was passed in for a little more versatility:
fun <T: Animal> maker(type: Class<T>): T {
return type.newInstance()
}
Correct version following your example (not sure if best approach overall):
fun <T: Animal> maker(type: Class<T>): T? {
return when (type) {
Cat::class.java -> Cat() as T
Dog::class.java -> Dog() as T
else -> null
}
}
And then to create objects:
val a = maker(Cat::class.java)
val b = maker(Dog::class.java)
(Updated) I am not an expert in Kotlin but you can do something like this :
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
class A {
fun greet() {
println("Hello A");
}
}
class B{
fun greet() {
println("Hello B");
}
}
fun <T : Any> maker(clazz: KClass<T>): T {
return clazz.createInstance();
}
val aObj = maker<A>(A::class);
aObj.greet();
val bObj = maker<B>(B::class);
bObj.greet();
Output:
Hello A
Hello B
I hope now it makes sense you just need to pass the class to the method and it returns an object.
As you will be using Animal as a parent class so you can replace Any => Animal
fun <T : Animal> maker(clazz: KClass<T>): T {
return clazz.createInstance();
}
If the function can be inline you can also use reified type
inline fun<reified T: Animal> make() = T::class.createInstance()
...
val dog = make<Dog>()
Please notice that to use createInstance() the class must have no-arg constructor or IllegalArgumentException will be thrown

Square brackets after function call

In the PagingWithNetworkSample, in the RedditActivity.kt on the line 68 is a function that contains another function call followed by square brackets and the class type (line 78):
private fun getViewModel(): SubRedditViewModel {
return ViewModelProviders.of(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val repoTypeParam = intent.getIntExtra(KEY_REPOSITORY_TYPE, 0)
val repoType = RedditPostRepository.Type.values()[repoTypeParam]
val repo = ServiceLocator.instance(this#RedditActivity)
.getRepository(repoType)
#Suppress("UNCHECKED_CAST")
return SubRedditViewModel(repo) as T
}
})[SubRedditViewModel::class.java]
}
What does this exactly do? Automatically cast to that type? (it's not an array/list to suppose it calls get)
Can you bring an example where this is useful?
That code might look strange, but it's really just a way of calling get(). This would be just as valid, but slightly more verbose:
private fun getViewModel(): SubRedditViewModel {
return ViewModelProviders.of(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
// ...
}
}).get(SubRedditViewModel::class.java)
}

How to return T in Kotlin?

I want to return T in the function.
I have an interface class IRepository.kt
interface IRepository
{
fun <T>Save(model:T)
fun <T>Delete(model:T)
fun <T>Get(id:Long):T
}
I want to implement in Repolmpl.kt
class Repolmpl:IRepository
{
override fun <T>Delete(model:T)
{
println("$model : Save}")
}
override fun <T>Get(id:Long):T
{
return T //ERROR here I want to return T...
}
override fun <T> Save(model: T)
{
println("$model : Delete")
}
}
I saw some similar questions online but I just can't find the right solution.
A generic type T is basically just a template. You cannot return it but have to replace it with an actual type first. Make the interface itself generic, not its methods. When implementing, specify T:
interface IRepository<T> {
fun save(model: T)
fun delete(model: T)
fun get(id: Long): T
}
class Repolmpl: IRepository<String>
{
override fun delete(model: String) {}
override fun get(id: Long): String {}
override fun save(model: String) {}
}
You cannot just return T. T is type here, and it is like return String.
You have to return instance of T. So, sth like:
class Repo {
val data = mapOf<Long, Any>()
// ...
fun <T> get(id: Long): T {
return data[id] as T // Get data from somewhere and then cast it to expected type
}
}