How to safely cast Lazy<*> to specific KClass<T> (Lazy<T>)? - kotlin

I need to get a fabric from fabric collection that will build a Lazy<T> instance, my method is below:
#Suppress("UNCHECKED_CAST") // Dirty hack
override fun <T : Any> resolveLazy(tag: String, clazz: KClass<out T>): Lazy<T> {
val fabric = fabrics[tag] ?: throw NeutrinoException("The `$tag` not found") // Get ILazyFabric
if (fabric !is ILazyFabric<*>) { // Ensure if the fabric is really ILazyFabric
throw NeutrinoException("Can't resolve `$tag` tag: the object is not lazy")
}
val obj = fabric.buildOrGet() // Get `Lazy<S>` object
return obj as Lazy<T> // Need to safely cast our Lazy<S> object to Lazy<T>
// And catch an exception if it's impossible
}
How to implement this method safely? Not to use #Suppress and throw a custom exception on fail.
P.S. I can't use reified.
UPD:
Thanks #Sweeper for the comment below, finally I've come to the next solution:
#Suppress("UNCHECKED_CAST")
override fun <T : Any> resolveLazy(tag: String, clazz: KClass<out T>): Lazy<T> {
val fabric = fabrics[tag] ?: throw NeutrinoException("The `$tag` not found")
if (fabric !is ILazyFabric<*>) {
throw NeutrinoException("Can't resolve `$tag` tag: the object is not lazy")
}
val obj = fabric.buildOrGet()
val castedObj = obj as? Lazy<T>
if (castedObj == null) {
val objType = obj::class.qualifiedName!!
val tType = clazz.qualifiedName!!
throw NeutrinoException(
"Can't resolve `$tag` tag: `Lazy<$objType>` can't be casted to `Lazy<$tType>`"
)
}
return castedObj
}

Related

Best way to implement a Reassignable Property Delegate

I impemented Reassignable Property Delegate
internal object UNINITIALIZED_VALUE
class SynchronizedReassignableImpl<out T>(private val initializer: () -> T,
private val expiredPredicate: (T) -> Boolean,
lock: Any? = null) : Reassignable<T> {
#Volatile
private var _value: Any? = UNINITIALIZED_VALUE
private val lock = lock ?: this
override val value: T
get() {
if (!isExpired()) {
#Suppress("UNCHECKED_CAST") (_value as T)
}
return synchronized(lock) {
val _v2 = _value
#Suppress("UNCHECKED_CAST")
if (_v2 !== UNINITIALIZED_VALUE && !expiredPredicate.invoke(_value as T)) {
_v2 as T
} else {
val typedValue = initializer()
_value = typedValue
typedValue
}
}
}
#Suppress("UNCHECKED_CAST")
override fun isExpired(): Boolean = !isInitialized() || expiredPredicate.invoke(_value as T)
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Reassignable value not initialized yet."
operator fun getValue(any: Any, property: KProperty<*>): T = value
operator fun getValue(any: Nothing?, property: KProperty<*>): T = value
}
fun <T> reassignable(initializer: () -> T, expiredPredicate: (T) -> Boolean, lock: Any? = null): SynchronizedReassignableImpl<T> {
return SynchronizedReassignableImpl(initializer, expiredPredicate, lock)
}
interface Reassignable<out T> {
val value: T
fun isInitialized(): Boolean
fun isExpired(): Boolean
}
This code declares the Delegate Property is working like lazy but on each getter's call, the predicate will be invoked to define a state of value (is expired or not). If the value is expired the one will be reassigned.
It's working, for example
class SynchronizedReassignableImplTests {
#Test
fun isReassignable() {
val initializer = { mutableListOf<String>() }
val expiredPredicate = { l: List<String> -> l.size == 2 }
val list by reassignable(initializer, expiredPredicate)
Assertions.assertEquals(0, list.size)
list.add("item ${list.size}")
Assertions.assertEquals(1, list.size)
list.add("item ${list.size}") // list size is 2 on next getter's call it will be reassigned
Assertions.assertEquals(0, list.size)
list.add("item ${list.size}")
Assertions.assertEquals(1, list.size)
}
}
but I'm working with Kotlin for only two days and think my solution not so beautiful.
Can somebody give me the advice to do this? Or maybe Kotlin has a native solution?

Kotlin + Arrow + Gson = None?

I have a model in Kotlin of a simple library of Books and Borrowers where a Book is checked out if it has a Borrower. I use Arrow Option to encode the absence/presence of a Borrower:
data class Borrower(val name: Name, val maxBooks: MaxBooks)
data class Book(val title: String, val author: String, val borrower: Option<Borrower> = None)
I am having trouble serializing/deserializing these objects to/from JSON in Gson - specifically the representation of an Option<Borrower> to a JSON null within a Book:
[
{
"title": "Book100",
"author": "Author100",
"borrower": {
"name": "Borrower100",
"maxBooks": 100
}
},
{
"title": "Book200",
"author": "Author200",
"borrower": null
}
]
My deserialize code:
fun jsonStringToBooks(jsonString: String): List<Book> {
val gson = Gson()
return try {
gson.fromJson(jsonString, object : TypeToken<List<Book>>() {}.type)
} catch (e: Exception) {
emptyList()
}
}
I get an empty list. The nearly identical jsonStringToBorrowers works fine.
Can someone point me in the right direction?
Would using a different JSON library like kotlinx.serialization or Klaxon be a better idea and how do they do the null <-> None thing?
Thank you!
The issue is a bit hidden by the fact that you don't log the exception before returning an empty list. If you logged that exception you would have gotten this:
java.lang.RuntimeException: Failed to invoke private arrow.core.Option() with no args
This means that Gson doesn't know how to create an Option class because it has no public empty constructor. Indeed, Option is a sealed class (hence abstract) having 2 concrete children classes: Some and None. In order to get an instance of Option you should use one of the factory methods, like Option.just(xxx) or Option.empty() among the others.
Now, in order to fix your code you need to tell Gson how to deserialize an Option class. To do that, you need to register a type adapter to your gson object.
A possible implementation is the following:
class OptionTypeAdapter<E>(private val adapter: TypeAdapter<E>) : TypeAdapter<Option<E>>() {
#Throws(IOException::class)
override fun write(out: JsonWriter, value: Option<E>) {
when (value) {
is Some -> adapter.write(out, value.t)
is None -> out.nullValue()
}
}
#Throws(IOException::class)
override fun read(input: JsonReader): Option<E> {
val peek = input.peek()
return if (peek != JsonToken.NULL) {
Option.just(adapter.read(input))
} else {
input.nextNull()
Option.empty()
}
}
companion object {
fun getFactory() = object : TypeAdapterFactory {
override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
val rawType = type.rawType as Class<*>
if (rawType != Option::class.java) {
return null
}
val parameterizedType = type.type as ParameterizedType
val actualType = parameterizedType.actualTypeArguments[0]
val adapter = gson.getAdapter(TypeToken.get(actualType))
return OptionTypeAdapter(adapter) as TypeAdapter<T>
}
}
}
}
You can use it in the following way:
fun main(args: Array<String>) {
val gson = GsonBuilder()
.registerTypeAdapterFactory(OptionTypeAdapter.getFactory())
.create()
val result: List<Book> = try {
gson.fromJson(json, TypeToken.getParameterized(List::class.java, Book::class.java).type)
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
println(result)
}
That code outputs:
[Book(title=Book100, author=Author100, borrower=Some(Borrower(name=Borrower100, maxBooks=100))), Book(title=Book200, author=Author200, borrower=None)]

Why am I getting a NullPointerException when fetching JSON?

I keep getting a NullPointerException at return place.
When I was debugging the app, the code skips the onFailure() and onResponse() methods.
Previously, this worked but I refactored it into the current classes.
class Repository private constructor() {
private val baseUrl: String = "http://api.openweathermap.org/"
val client = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY))
.build()
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(MoshiConverterFactory.create())
.client(client)
.build()
val networkApi = retrofit.create(NetworkApi::class.java)
private object Holder { val INSTANCE = Repository() }
companion object {
val instance: Repository by lazy { Holder.INSTANCE }
}
fun fetchWeatherData(placeName: String): Place {
var place: Place? = null
val call: Call<Place> = networkApi.getPlaceWeather(placeName)
call.enqueue(object : Callback<Place> {
override fun onFailure(call: Call<Place>?, t: Throwable?) {
println(t?.message)
}
override fun onResponse(call: Call<Place>?, response: Response<Place>?) {
if (response != null && response.isSuccessful && response.body() != null) {
place = response.body() as Place
println(place.toString())
}
}
})
return place!!
}
}
class MainPresenter(private val view: MainContract.View, val context: Context) : MainContract.Presenter {
val repository = Repository.instance
...
override fun updateListOfPlaces() {
var places = mutableListOf<Place>()
for (index in 0 until favPlaceStrings.size) {
places.add(repository.fetchWeatherData(favPlaceStrings.elementAt(index)))
}
view.showFavouritePlaces(places)
}
}
The way you're using retrofit makes it have an asynchronous behaviour, meaning the code within onFailure and onResponse might run after or before you have a chance to return from fetchWeatherData. In other words, you cannot assume that place will have a value when you return from fetchWeatherData and this is actually what's happening, place is still null and calling !! will cause the null pointer exception you're having.
To fix this you either change the way you're using retrofit to be synchronous, or you use an approach like callbacks.
Personally, I prefer the callback approach / reactive streams and this you can check here.
Making the code synchronous will most likely lead to other issues such as network calls on the main thread, which are not allowed and crash the app.
You need to invert your logic. You cannot simply "return data" from a network call that you're waiting for
Instead, loop over the list, make requests, then show/update the view
for (index in 0 until favPlaceStrings.size) {
val call: Call<Place> = networkApi.getPlaceWeather(favPlaceStrings.elementAt(index))
call.enqueue(object : Callback<Place> {
override fun onFailure(call: Call<Place>?, t: Throwable?) {
println(t?.message)
}
override fun onResponse(call: Call<Place>?, response: Response<Place>?) {
if (response != null && response.isSuccessful && response.body() != null) {
val place: Place = response.body() as Place
places.add(place) // move this list to a field
println(place.toString())
view.showFavouritePlaces(places) // this is fine that it's inside a loop
}
}
})
}

kotlin, got “Type mismatch. Required: Disposable? Found: Unit” when using observer object instance in the subscribe()

Edit:
based on Dmitry Ikryanov's suggestion,
using DisposableObserver will compile, but it causes crash
io.reactivex.exceptions.ProtocolViolationException: It is not allowed to
subscribe with a(n) com.DataManager$theObserver$1 multiple times. Please
create a fresh instance of com.DataManager$theObserver$1 and subscribe that
to the target source instead.
the only code of subecribWith(), which has been called only once
fun initSession() {
if (mDisposable != null && mDisposable!!.isDisposed) {
mDisposable!!.dispose()
}
mDisposable = RxBus.listen(DataEvent::class.java).subscribeWith(theObserver) <=== crash at here
}
the DisposableObserver is a member variable of the class:
var theObserver: DisposableObserver<DataEvent> = object : DisposableObserver<DataEvent>() {
override fun onComplete() {
Log.e(TAG, "onComplete: All Done!") }
override fun onNext(t: DataEvent) {
Log.e(TAG, "Next: " + t)
onDataReady(t) }
override fun onError(e: Throwable) {
Log.e(TAG, "onError: ")
}
}
===
Original question:
trying to use RxJava subscribe() in kotlin, get an error “Type mismatch. Required: Disposable? Found: Unit”, not sure what it means, anyone knows?
class DataEvent {}
using RxBus
object RxBus {
private val publisher = PublishSubject.create<Any>()
fun publish(event: Any) {
publisher.onNext(event)
}
// Listen should return an Observable and not the publisher
// Using ofType we filter only events that match that class type
fun <T> listen(eventType: Class<T>): Observable<T> = publisher.ofType(eventType)
}
when call like this, it is ok:
mDisposable = RxBus.listen(DataEvent::class.java).subscribe({
onDataReady(it)
})
but when call the RxBus.listen(DataEvent::class.java).subscribe(observer) with defined observer instance
it shows red underline: “Type mismatch. Required: Disposable? Found: Unit”
mDisposable = RxBus.listen(DataEvent::class.java).subscribe(observer)
the observer is:
var observer: Observer<DataEvent> = object : Observer<DataEvent> {
override fun onSubscribe(d: Disposable) {
Log.e(TAG, "onSubscribe: ")
}
override fun onNext(#NonNull t: DataEvent) {
Log.e(TAG, "onNext: " + t)
onDataReady(t)
}
override fun onError(e: Throwable) {
Log.e(TAG, "onError: ")
}
override fun onComplete() {
Log.e(TAG, "onComplete: All Done!")
}
}
It's because in RxJava 2.0 method subscribe(observer) was changed and return nothing.
Unlike the Observable of version 1.x, subscribe(Observer) does not allow external cancellation of a subscription and the Observer instance is expected to expose such capability.
You can use subscribeWith(observer).
Example:
val disposable = Observable.just("Hello world!")
.delay(1, TimeUnit.SECONDS)
.subscribeWith(object : DisposableObserver<String>() {
public override fun onStart() {
println("Start!")
}
fun onNext(t: Int?) {
println(t)
}
override fun onError(t: Throwable) {
t.printStackTrace()
}
override fun onComplete() {
println("Done!")
}
})

Kotlin nullable variable assignment

In Kotlin, is there any shorter syntax for this code:
if(swipeView == null){
swipeView = view.find<MeasureTypePieChart>(R.id.swipeableView)
}
First i tried this:
swipeView ?: view.find<MeasureTypePieChart>(R.id.swipeableView)
but then i realised that wasn't an assignment, so that code does nothing. Then i tried:
swipeView = swipeView ?: view.find<MeasureTypePieChart>(R.id.swipeableView)
Which works, but it a bit verbose. I would expect something like this:
swipeView ?= view.find<MeasureTypePieChart>
But unfortunately that doesn't work. Is there any way of accomplish this with a short syntax?
I know i can do this:
variable?.let { it = something } which works.
Shorter syntax would be to avoid swipeView from ever being null.
Local variable
If swipeView is a local variable then you can declare it non-null when initially assigning it:
val swipeView = ... ?: view.find<MeasureTypePieChart>(R.id.swipeableView)
Function argument
If swipeView is a function argument then you can use a default argument to ensure it is never null:
fun something(swipeView: View = view.find<MeasureTypePieChart>(R.id.swipeableView))
Class property
Read-only
If swipeView is a read-only class property (i.e. val) then you can use Kotlin's built-in Lazy:
val swipeView by lazy { view.find<MeasureTypePieChart>(R.id.swipeableView) }
Mutable
If swipeView is a mutable class property (i.e. var) then you can define your own delegate similar to Lazy but mutable. e.g. The following is based on kotlin/Lazy.kt:
interface MutableLazy<T> : Lazy<T> {
override var value: T
}
fun <T> mutableLazy(initializer: () -> T): MutableLazy<T> = SynchronizedMutableLazyImpl(initializer)
fun <T> mutableLazy(lock: Any?, initializer: () -> T): MutableLazy<T> = SynchronizedMutableLazyImpl(initializer, lock)
operator fun <T> MutableLazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
operator fun <T> MutableLazy<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
private object UNINITIALIZED_VALUE
private class SynchronizedMutableLazyImpl<T>(initializer: () -> T, lock: Any? = null) : MutableLazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
#Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override var value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
#Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
#Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
set(value) {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
_value = value
} else synchronized(lock) {
_value = value
initializer = null
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "MutableLazy value not initialized yet."
}
Usage:
var swipeView by mutableLazy { view.find<MeasureTypePieChart>(R.id.swipeableView) }
The initializer will only be called if swipeView is read and is not initialized yet (from a previous read or write).