Kotlin inner class accessing outer class? - android

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

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

Why are non-null type properties of class obtained from WeakReference.get() changing to nullable

I am trying to write a Handler in an Activity that needs a reference to the activity. If I write like this
class MainActivity : AppCompatActivity() {
private val mHandler = MainActivityHandler(this)
class MainActivityHandler(val activity: MainActivity) : Handler() {
override fun handleMessage(msg: Message?) {
when(msg?.what) {
MSG_CHANGE_TEXT -> {
activity.tv_logged.setText(R.string.title_main)
activity.mHandler.sendMessageDelayed(obtainMessage(SOMETHING), 3000)
}
// ...
}
}
}
}
This code compiles and works as expected. But if I try to pass a weak reference to the activity like this
class MainActivity : AppCompatActivity() {
private val mHandler = MainActivityHandler(WeakReference(this))
class MainActivityHandler(val activityRef: WeakReference<MainActivity>) : Handler() {
private val activity
get() = activityRef.get()
override fun handleMessage(msg: Message?) {
when(msg?.what) {
MSG_CHANGE_TEXT -> {
activity?.tv_logged.setText(R.string.title_main)
activity?.mHandler.sendMessageDelayed(obtainMessage(SOMETHING), 3000)
}
// ...
}
}
}
}
Now the compiler complains that tv_logged and mHandler are nullable receiver type and need to be accessed using ?.
I can understand that the val activity: MainAcitivity? inside the handler is nullable because it comes from WeakReference.get() but how come the properties in MainActivity are also nullable?
Its because the return type of activity?.tv_logged is (assuming its a TextView ) , TextView? . In Kotlin docs , where an alternative is proposed to checking null via if condition
Your second option is the safe call operator, written ?.
b?.length
This returns b.length if b is not null, and null otherwise. The type of this expression is Int? .
To perform a certain operation only for non-null values, you can use the safe call operator together with let:
activity?.let{ //access activity via `it`}
I understood this when I read the whole text at https://kotlinlang.org/docs/reference/null-safety.html#safe-calls
This has nothing to do with WeakReference as I suspected. It happens because safe call operator returns a nullable type even when accessing non-nullable type properties. (The doc doesn't really specify this as explicitly and clearly.)

Remove "this" callback in kotlin

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

Categories

Resources