Remove "this" callback in kotlin - android

I'm a bit kotlin newbie and I'm trying to remove the callback instance inside the callback itself.
What I'm trying to achieve it's something similar to the following code.
private val myCallback = SomeInterfaceType {
if(it.something) {
someObject.removeListener(this#SomeInterfaceType)
}
}
Of course it doesn't compile or else I wouldn't be asking here. So I ask, how to remove the callback from inside the instance of the interface?
edit:
the error is "inferred type is X but Y was expected.
edit 2: I just realized I've asked the wrong question, it's similar to it but not exactly a Interface.
The object I'm using have the following constructor/interface
public open class Watcher<T> public constructor(call: (T) -> kotlin.Unit)
so in reality I'm trying to reference the Watcher from inside the call: (T) -> kotlin.Unit to remove the listener.
Is that possible?

You need to use a full object expression syntax to refer to be able to refer to the instance itself:
private val myCallback = object: SomeInterfaceType() {
override fun onSomeEvent() {
if (it.something) {
someObject.removeListener(this)
}
}
}

There's also a workaround: wrap the reference to myCallback into a lambda passed to a function that calls it (e.g. run { ... }):
private val myCallback: SomeInterfaceType = SomeInterfaceType {
if (it.something) {
someObject.removeListener(run { myCallback })
}
}

Related

Is it possible to call a non-final function in constructor?

I'm developing a huge section of my Android app in Jetpack Compose with the MVVM pattern.
I have a ViewModel father that is extended by all the other ViewModels. There, I have defined an open function which contains the initialization logic of each ViewModel that I need to call every time I enter in a new screen and to call again when something went wrong and the user clicks on the "try again" button.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
fun onTryAgainClick() {
myInitMethod()
}
}
class MyScreen1ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
class MyScreen2ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
Is there a way I can call this method in the init function of MyFatherViewModel instead of doing it in all the children ViewModels? If I try to do that, it gives me the "Calling non-final function in constructor" warning and, of course, it doesn't work.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
init {
myInitMethod()
}
fun onTryAgainClick() {
myInitMethod()
}
}
Is it possible to call a non-final function in constructor?
Technically yes, but you shouldn't. Kotlin is trying to protect you from problems here. If you call an open function from a constructor, it means you are running code from the child class before the parent class is completely initialized, and before the child class even started initializing. If the child implementation of the open function tries to access properties from the child class, unexpected things may happen. For instance, non-nullable properties could yield null (because not initialized yet), or primitive values could yield their type's default instead of the default value from their initializer:
fun main() {
Child()
}
open class Parent {
init {
initialize()
}
val id = 42
open fun initialize() = println("parent init")
}
class Child : Parent() {
val name = "Bob"
override fun initialize() = println("initializing $name, parent id=$id")
}
This prints the following output:
initializing null, parent id=0
I guess you can see why this is dangerous.
Maybe you should reconsider what you're trying to do with this try-again feature. Maybe a new view model should be instantiated instead (if try-again is to handle crashes, the state of the current view model may actually be bad enough to want to re-create it from scratch anyway).

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
}
}
}

Android kotlin method accept any object type as parameter

I want to pass 2 different object types to a method to update the view. How can I have this method
accept 2 different object types and access them instead of having 2 different methods for 2 different object types.
I needed something like this -
fun updateView(object: Any<T>) {
//Access the objects here to update the view
}
fun <T : Any> updateView(obj: T) {
//Access the objects here to update the view
}
OR
fun updateView(obj: Any ?= null, obj2:Any ?= null) {
// Access the objects here to update the view
// pass check nullity and use which you want (or not null), other parameter will remain null
obj?.let {
it...
}
obj2?.let {
it...
}
}
Call
updateView(obj1, obj2)
// OR
updateView(obj2 = myObj2)
You can use interfaces for this:
interface ViewInterface {
fun action()
}
class ObjectA : ViewInterface {...}
class ObjectB : ViewInterface {...}
fun updateView(ob: ViewInterface) {
ob.action()
}
try something like this
fun updateView(variable1: Any? = null, variable2:Any? = null) {
//Access the objects here to update the view
}
using named parameters, you can then just set the variables you need when calling the method:
updateView(variable1 = "something")
updateView(variable2 = "something else")
Have your 2 objects implement the same interface or inherit from the same superclass then do something like:
fun updateView(object: MyInterface) {
...
}
Use polymorphism
fun updateView(object: X) {
...
}
fun updateView(object: Y) {
...
}
You can pass two types of object by this way
fun updateView(data:Any? = null,data2:Any?=null) {
//Cast Your Object To your desired type and also can pass null too
// Access the objects here to update the view
}
I would advise separating the two functions, or to use inheritance of some sort to use a single function. But as I see none of those above (which are correct, from the SOLID point of view) satisfies your request, you can just check inside the function based on class.
fun updateView(object: Any) {
when(object){
is Class1Type -> // do whatever fits for the first case
is Class2Type -> // do whatever fits for the second case
else -> // etc.
}
}
The best solution is not related to Kotlin at all. Just make both of them implement an interface and use this interface as the function parameter type.
In general, accepting Any as an input type is not a good practice and using generics is an overkill.
interface DoesStuff
class DoesStuffA: DoesStuff { }
class DoesStuffB: DoesStuff { }
fun doStuff(doer: DoesStuff) {
// do stuff
// if need to distinguish between types
when (doer) {
is DoesStuffA -> // do A
is DoesStuffB -> // do B
}
}

Kotlin inner class accessing outer class?

How do I call a method in outer class from the inner class?
I can pass it as context, but I can't call methods on it
loginButton.setOnClickListener {
ssoManager.login(emailEditText.text.toString(), passwordEditText.text.toString())
.subscribe(object: Consumer<SSOToken> {
val intent = Intent(this#LoginActiviy, PasscodeActivity::class.java)
this#LoginActiviy.startActivity(intent)
})
I'm not sure what APIs you're using here, I'm gonna assume that your Consumer is java.util.function.Consumer for the sake of the answer.
You are writing code directly in the body of your object, and not inside a function. The first line of creating the Intent only works because you're declaring a property (and not a local variable!).
What you should do instead is implement the appropriate methods of Consumer, and write the code you want to execute inside there:
loginButton.setOnClickListener {
ssoManager.login()
.subscribe(
object : Consumer<SSOToken> {
val foo = "bar" // this is a property of the object
override fun accept(t: SSOToken) {
val intent = Intent(this#LoginActiviy, PasscodeActivity::class.java)
this#LoginActiviy.startActivity(intent)
}
}
)
}

Kotlin Android Button.onClickListener causing NoSuchMethodError

I think I've found a quirk of using kotlin for android, or there's some gap in my understanding of the syntax.
Trying to set an onClickListener for a button is throwing a NoSuchMethodError
Here's the code at fault
button.setOnClickListener(Button.OnClickListener {
fun onClick(view: View){
val intent : Intent = Intent(this,DetailActivity::class.java)
if(obj is String) {
intent.putExtra("Topic", obj)
}
startActivity(intent)
}
})
And here's the stacktrace outputted
java.lang.NoSuchMethodError: No static method OnClickListener(Lkotlin/jvm/functions/Function1;)Landroid/view/View$OnClickListener; in class Landroid/widget/Button; or its super classes (declaration of 'android.widget.Button' appears in /system/framework/framework.jar:classes2.dex)
Anyone know whats up?
Interestingly, I don't get that error, your code compiles for me. However, it won't work for a different reason: you're passing in a lambda as the listener inside the {}, which means that the contents of it will be executed when the click event happens. There is no code to run inside it though, you're just defining a local function named onClick that will never be called.
button.setOnClickListener(Button.OnClickListener {
fun onClick(view: View){
...
}
Log.d("TAG", "hi") // this is the code that would be executed on click events
})
There are two ways to fix your syntax:
First, you can use an object expression to create the listener, this is pretty close to what you wrote, and is along the lines of the classic Java solution, it explicitly creates an anonymous class (note that the OnClickListener interface is actually under the View class):
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
val intent = ...
}
})
Or you can use the shorter, more Kotlin-like syntax that the IDE will suggest when you try using the previous long form anyway, by making use of SAM conversion:
button.setOnClickListener {
val intent = ...
}
This solution uses a lambda just like your initial code did, it just doesn't name what interface it converts to explicitly, and drops the () which are not required a single lambda parameter.
try
button.setOnClickListener {
// Handler code here
}
You can try :
// case 1
button?.setOnClickListener { view ->
// handler here
}
// case 2
button?.setOnClickListener {
// you can use keyword 'it' for use member view
// handler here
}

Categories

Resources