Does Kotlin `by` keyword work for nullable delegate? - android

I've pretty excited by Kotlin compiler features and by by in particular - it saves time generating gelegating code:
https://kotlinlang.org/docs/reference/delegation.html
But i want delegate to be nullable and delegating code to check if it's null first and return if it is:
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int?) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base?) : Base by b {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
When compiling ^ i'm getting Type mismatch: inferred type is Base? but Base was expected.
Is it still possible with Kotlin?
To be more detailed i'd like Kotlin compiler to generate forwarding calls to wrapped impl (extWebChromeClient) in https://developer.android.com/reference/android/webkit/WebChromeClient like follows:
private WebChromeClient intWebChromeClient = new WebChromeClient()
{
#Override
public void onReceivedTitle(WebView view, String title)
{
if (extWebChromeClient != null)
{
extWebChromeClient.onReceivedTitle(view, title);
}
}
...

You can make this yourself using dynamic proxies, though I wouldn't really recommend it. Note that for non-void methods there's no way to require overriding them. The below implementation just throws exceptions for them unconditionally, but you could still call them for non-null x.
inline fun <reified T : Any> nullableProxy(x: T?): T {
val handler = InvocationHandler { _, method, args ->
if (method.returnType == Void.TYPE) {
if (x != null) {
method.invoke(x, *(args ?: arrayOf()))
}
} else
throw UnsupportedOperationException("Non-void method")
}
return Proxy.newProxyInstance(
T::class.java.classLoader,
arrayOf(T::class.java),
handler) as T
}
class Derived(b: Base?) : Base by nullableProxy(b)
This also won't perform as well as implementing methods directly would.

Related

What are the differences between kotlin variable functions and common fun functions?

There is a fun method0:
private fun method0() {
println("method0 fun")
}
And a var method0 :
var method0 = {
println("method0")
}
It seems they are used the same:
method0()
I found that both occur at the same time, and the fun function has a higher priority when the code calls.
Other than that, is there any difference between them?
The var way of doing it results in a functional object. The lambda content is wrapped as a functional object so it can be passed around like any other instance of a class. It can directly be used as a function parameter, for instance.
var method0 = {
println("method0")
}
fun doSomethingTwice(action: ()->Unit) {
repeat(2) { action() }
}
fun main() {
doSomethingTwice(method0)
}
And since it's marked as a var you can swap it out for a different function:
fun main() {
method0 = { println("hello, world!") }
doSomethingTwice(method0)
}
Note that this way of specifying a function is a little bit heavier since it is wrapping the function in another class instance.
And you can still wrap any "regular" function into a functional object at any time by using :: to avoid doing it until it's necessary.
fun method0() {
println("method0")
}
fun main() {
doSomethingTwice(::method0)
}

Kotlin recursive problem when type checking

I have the following code which i think is valid, because the recursion happens as a result of a callback. It's not called directly as a result of the function call. But the compiler seems to think there is a recursion issue
class Model(callBack: CallBack) {
interface CallBack {
fun onSomething()
}
}
class SomeClass {
fun createModel() = Model(callBack)
val callBack = object : Model.CallBack {
override fun onSomething() {
val anotherModel = createModel()
// Use model for something
}
}
}
Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
Is there a workaround for this?
EDIT
I also tried changing callBack to a function so that the same instance is not referenced by multiple models, but I get the same error
The recursive problem mentioned is not about function calls, it's about the compiler trying to find out the types of the declaration and it has stuck in a recursive type checking. It wants to find the output type of createModel which depends on the type of val callback and it depends on createModel again. As it says, declare their types to fix the issue.
class Model(callBack: CallBack)
{
interface CallBack {
fun onSomething()
}
}
class SomeClass {
fun createModel() : Model = Model(callBack)
val callBack : Model.CallBack = object : Model.CallBack {
override fun onSomething() {
val anotherModel : Model = createModel()
// Use model for something
}
}
}

Kotlin - Generic type safe, wrong warning? java.lang.ClassCastException

I'm fetching response from some API, after getting the response I converting it to List of my required Object e.g:
fun <T> getAsList(input: String): ArrayList<T> {
val objType = object : TypeToken<ArrayList<T>>() {}.type
val result = Gson().fromJson(input, objType) as ArrayList<T>
println(result[0]) // <-- no warning here !! It's work
println("result: " + result.toString()) // Also it's work here
return result
}
Then I pass this list to somewhere e.g:
updateFromDownload(getAsList<T>(resultValue))
And by override this method I can get the result, e.g:
override fun updateFromDownload(result: List<Response>?) {
val listTest = ArrayList<Response>()
listTest.add(result!![0]) // <-- This work
println(listTest)
println("resss:" + result[0]) // <-- This not work !!!
for (response in result){
// The loop crash too, not work
}
As demonstrated above, adding to listTest work fine, but If I tried to get the element individually like I did inside getAsList() It's crash due to:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to ......Response
Can I access the result directly without fill it to new list?
Edit- Whole cycle for code:
class ConnectToURL<T>(callback: DownloadCallback<T>) : AsyncTask<String, Int, ConnectToURL.Result>() {
private var mCallback: DownloadCallback<T>? = null
init {
setCallback(callback)
}
override fun onPostExecute(result: Result?) {
mCallback?.apply {
result?.mException?.also { exception ->
//val gson = Gson().fromJson(result.mResultValue!!, type)
//updateFromDownload(gson)
return
}
result?.mResultValue?.also { resultValue ->
updateFromDownload(getAsList<T>(resultValue))
return
}
finishDownloading()
}
}
}
And I Invoke ConnectToURL from:
class BuilderClass<T> private constructor(
callback: DownloadCallback<T>,
private val url: String
) {
init {
ConnectToURL(callback).execute(url)
}
/// some code . . .. .
fun build() = BuilderClass(callback, url)
}
}
Then I override the updateFromDownload function as it's part from DownloadCallback
The generic type T is erased at compile time, so the type information is not present at runtime.
object : TypeToken<ArrayList<T>>() {}.type
Thats the reason Gson does not convert to the Response class.
You could use inline plus reified to avoid type erasure.
inline fun <reified T> getAsList(input: String): ArrayList<T>

How to check if the result of an expression is used in a custom Android lint rule

I'm trying to write a lint rule to catch places where the result of an RxJava2 function is not used in anyway. For example:
final Observable<String> observable = getObservable();
observable.subscribe(this::onSuccess, this::onError);
In RxJava2, the subscribe function returns a Disposable that should be used to unsubscribe if the program/class instance "finishes" in some way in order to prevent memory leaks. I want to fail my build if any occurences like this are found.
This particular method (and all of the other ones I'm interested in) is annotated with io.reactivex.annotations.CheckReturnValue:
#CheckReturnValue
#SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {
return subscribe(onNext, onError, Functions.EMPTY_ACTION, Functions.emptyConsumer());
}
My plan is to write a custom lint rule that:
Searches for expressions that return the result of a method annotated with io.reactivex.annotations.CheckReturnValue
Filter the searches down to only expressions whose result is never used
For example, here are some cases that should not fail:
final CompositeDisposable compositeDisposable = new CompositeDisposable();
// Result of subscribe passed into another function
compositeDisposable.add(observable.subscribe(this::onSuccess, this::onError).dispose());
// Result of subscribe stored in a variable
final Disposable disposable = observable.subscribe(this::onSuccess, this::onError);
// Result of subscribe used
observable.subscribe(this::onSuccess, this::onError).dispose();
I've managed to write a lint rule that finds instances of call expressions where the result is annotated with CheckReturnValue, but I'm struggling to figure out how to use the JetBrains UAST/PSI APIs to work out if the result is used. This is my rule so far:
class RxJava2CheckReturnValueMethodNotAssigned : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
override fun createUastHandler(context: JavaContext) = CheckReturnValueVisitor(context)
class CheckReturnValueVisitor(private val context: JavaContext) : UElementHandler() {
override fun visitCallExpression(node: UCallExpression) {
val method = node.resolve() ?: return
if (!isCheckReturnValueAnnotatedMethod(method)) {
return
}
if (!isResultOfCallUsed(node)) {
return
}
reportIssue(node)
}
private fun isCheckReturnValueAnnotatedMethod(method: PsiMethod): Boolean {
return context.evaluator.getAllAnnotations(method, true)
.any { "io.reactivex.annotations.CheckReturnValue" == it.qualifiedName }
}
private fun isResultOfCallUsed(node: UCallExpression): Boolean {
// Need to check is the result of the expression is used in some way
return false
}
private fun reportIssue(node: UCallExpression) {
// SNIP...
}
}
}
This currently doesn't work because it reports all usages of any function annotated with CheckReturnValue.
As far as I know, node.resolve() often return null

Kotlin - Check to see if the generic parameter is optional or not?

I am writing this generic method to fetch data from firebase? In some cases getting null back is valid, in other cases is not, is it possible to check to see if the generic param is nullable or not?
ex
reference.obsrveObject(User.class)
should throw if null
reference.obsrveObject(User?.class)
should call onNext with null value
fun DatabaseReference.observeSingleEvent(): Observable<DataSnapshot?> {
return Observable.create { subscriber ->
val valueEventListener = object: ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot?) {
subscriber.onNext(snapshot)
subscriber.onCompleted()
}
override fun onCancelled(error: DatabaseError?) {
subscriber.onError(FirebaseDatabaseThrowable(error))
}
}
addListenerForSingleValueEvent(valueEventListener)
}
}
fun <T>DatabaseReference.obsrveObject(clazz: Class<T>): Observable<T> {
return observeSingleEvent().map { snapshot ->
if (snapshot != null) {
snapshot.getValue(clazz)
}
else {
// if clazz is nullable return null
// if clazz is not nullabel throw
throw Exception("")
}
}
}
Note: I've not personally used Firebase yet so some of the examples below may not compile but should be close enough.
Neither Class<T> nor KClass<T> track nullability as they only represent a class. A KType, however, can represent "a class with optional type arguments, plus nullability" and has an isMarkedNullable property.
You can use reified type parameters to get a KClass for the generic type but you cannot get (as of Kotlin 1.1) a KType. However, you can still check to see if the generic type is nullable (nullable reified type : Kotlin) with null is T (thanks to sosite for pointing out that wrapping null as T in a try/catch is not necessary).
With this, as long as you can mark obsrveObject as inline, you can "check to see if the generic parameter is optional or not":
inline fun <reified T> DatabaseReference.obsrveObject(): Observable<T> {
return observeSingleEvent().map { snapshot ->
if (snapshot != null) {
snapshot.getValue(T::class.java)
} else if (null is T) {
null as T
} else {
throw Exception("")
}
}
}
Usage:
databaseReference.obsrveObject<User>() // Observable<User>
databaseReference.obsrveObject<User?>() // Observable<User?>
If you cannot use an inline function (and therefore reified type parameters) then you need to find a way to get a KType.
You can get a KType from the returnType on KCallable<R> but you can also create a KType from a KClass<T> using createType:
User::class.createType(nullable = false) // User
User::class.createType(nullable = true) // User?
The class here is the type's classifier so depending on how you are using obsrveObject you might change its argument type from Class<T> to KCallable<T>. You could change it to a KType directly and create instances as needed but I'm guessing your grabbing clazz currently from the return type of a property so I would go with KCallable<T>:
fun <T : Any> DatabaseReference.obsrveObject(callable: KCallable<T>): Observable<T?> {
val kType = callable.returnType
val kClass = kType.classifier as KClass<T>
val clazz = kClass.java
return observeSingleEvent().map { snapshot ->
if (snapshot != null) {
snapshot.getValue(clazz)
} else if (kType.isMarkedNullable) {
null
} else {
throw Exception("")
}
}
}
You would then call it using a reference to a callable (property, function, etc.):
databaseReference.obsrveObject(session::user)

Categories

Resources