Kotlin - Inheritance in Object expression - android

I need to inherit the Object A from Object B, were both the objects consist of constants only.
Example
Object A {
const val a1 = "some_data_1"
const val a2 = "some_data_2"
}
Object B : A {
const val b1 = "some_data_3"
}
is it feasible to achieve this in kotlin ?

Kotlin is an object-oriented programming (OOP) language. We can inherit object A from object B for that we have to to allow class "A" to be inherited, for that we need to attach the open modifier before the class to make it non-final.
For the const we have to use companion object, which is an object that is common to all instances of that class.
open class A {
companion object {
const val a1 = "some_data_1"
const val a2 = "some_data_2"
}
}
class B : A() {
companion object {
const val b1 = "some_data_3"
}
val a_1 = a1
val a_2 = a2
}
Check this link to understand inheritance
Check this link to understand Companion Object

open class A {
companion object {
const val a1 = "some_data_1"
const val a2 = "some_data_2"
}
}
class B : A() {
companion object {
const val b1 = "some_data_3"
}
val a_1 = a1
val a_2 = a2
}

for a class to be inherited in Kotlin it should be open for example open class A {}
for class B to extends class A should add the class B : A()
for constants should be inside a companion object {}

I would probably dive a bit deeper.
Object in your example is an Object Declaration.
You should have a look at this doc describing Object Declarations and Object Expressions.
The question is - Why would you need to have one class only with constants extend another(also containing only const vals)?
Object Declarations are Kotlin built in Singletons and BTW are thread safe.
Example :
object DeviceProvider {
private val _devices = ArrayList<Device>()
fun getDevices() = _devices as List<Device>
fun registerDevice(device: Device) {
_devices.find { it == device } ?: _devices.add(device)
}
}
Usage :
fun addDevice(){
ServiceProvider.registerDevice(Device("1234"))
}
Object declarations are allowed to extend open classes and interfaces - so you can define a behavior or even a state via inheritance. As usual you can have a look at Kotlin docs about inheritance, those are exhaustive and nice read.
Still if we are talking about common approaches defining const values - then separate file is the best solution, if of course that value should be bound to any specific class. Here is a nice point of view(thanks Marko for your answer) :
In Java you're forced to put all static field and method declarations
in a class and often you even have to create a class just for that
purpose. Coming to Kotlin, many users look for the equivalent facility
out of habit and end up overusing companion objects.
Kotlin completely decouples the notions of a file and a class. You can
declare any number of public classes is the same file. You can also
declare private top-level functions and variables and they'll be
accessible only to the classes within the same file. This is a great
way to organize closely associated code and data.

Related

Android + Kotlin + Hilt: Injecting into static methods classes

I'm not very clear about the best way to inject into a static methods helper class (lets say a Custom class).
I'm kinda new to Kotlin, and as I've learnt we can access a method statically in two ways:
Object class.
Class + companion object.
To start, I'm not sure which one is the most recommended one (if there is a best practice regarding this), but my "problem" arises when needing to inject dependencies into a static method class.
Let's go with a simple example:
I have a static methods class called AWUtils (not decided if it should be an object class or a class with companion object yet though, and this will most likely depend on the injection mechanism recommended) with the next method:
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
Here, app is the instance of my AppSettings class which holds all app configuration so, as you see setAmpersand2Yellow needs AppSettings, and of course I would't pass it as a parameter by any means, so it's a AWUtils dependence.
Using AWUtils as a class with companion object for the static methods I cannot inject directly AppSettings into company object as far as I know (at least I cannot do constructor injection, let me know if I'm wrong) and if I inject into companion object parent class (AWUtils) constructor then I don't know how to access those dependences from the companion object itself (the child).
If I use fields injection in AWUtils as a class then it complains than lateinit field has not been initialised and I don't know how to deal with this, because as far as I know lateinit fields are initialised in onCreate, which does not exist in this kind of classes.
One other possibility is to use an object with fields and set the dependencies values from caller in a static way before calling the method, for example:
object AWUtils {
var app: AppSettings? = null
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
#AndroidEntryPoint
class OtherClass
#Inject constructor(private val app: AppSettings) {
fun AnyFunction() {
var mystr = "whatever"
AWUtils.app = app
var yellowStr = AWUtils.setAmpersand2Yellow(myStr)
}
}
In the end, I'm not sure on how to supply dependencies to a static methods class and which form of "static" class should I choose.
Edit 1:
Apart from my ApSettings class, I need a context, like for example in this next isTablet method:
val isTablet: String
get() {
return ((context.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
}
In the end, I need a context and my AppSettings (or any other custom classes) to be injected anyway in a class with static methods.
Edit 2:
I could do (from the activity):
AWUtils.context = this
AWUtils.app = app
var isTablet = AWUtils.isTablet
And it works, but rather to be in the need of assigning a value to two fields (or more) every time I need to call a static method, I would prefer the fields to be injected in any way.
That's what dependency injection is meant for, isn't it?
Edit 3: I'm starting to be fed up with Hilt, what is supposed would have been created to simplify our life, only makes our programming life much more complicated.
As you clarified in the comments, you want to have your utils class accessible in an easy way across your codebase, so this answer will focus on that and on your original questions.
I'm kinda new to Kotlin, and as I've learnt we can access a method statically in two ways: Object class or Class + companion object.
Kotlin does not have Java-style statics. One reasoning behind it was to encourage more maintainable coding practices. Static methods and static classes are also a nightmare for testing your code.
In Kotlin you would go with an object (but a class + companion object would work in the same way)
object AWUtils {
lateinit var appContext: Context
lateinit var appSettings: AppSettings
fun initialize(
appContext: Context,
appSettings: AppSettings,
// more dependencies go here
) {
this.appContext = appContext
this.appSettings = appSettings
// and initialize them here
}
val isTablet: Boolean
get() = ((appContext.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
appSettings.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
Since this object should be accessible across the whole application it should be initialized as soon as possible, so in Application.onCreate
#HiltAndroidApp
class Application : android.app.Application() {
// you can inject other application-wide dependencies here
// #Inject
// lateinit var someOtherDependency: SomeOtherDependency
override fun onCreate() {
super.onCreate()
// initialize the utils singleton object with dependencies
AWUtils.initialize(applicationContext, AppSettings())
}
Now anywhere in your app code you can use AWUtils and AppSettings
class OtherClass { // no need to inject AppSettings anymore
fun anyFunction() {
val mystr = "whatever"
val yellowStr = AWUtils.setAmpersand2Yellow(myStr)
// This also works
if (AWUtils.isTablet) {
// and this as well
val color = AWUtils.appSettings.drawerFooterColor
}
}
}
There is another way in Kotlin to write helper/util functions, called extension functions.
Your isTablet check might be written as an extension function like this
// This isTablet() can be called on any Configuration instance
// The this. part can also be omitted
fun Configuration.isTablet() = ((this.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
// This isTablet() can be called on any Resources instance
fun Resources.isTablet() = configuration.isTablet()
// This isTablet() can be called on any Context instance
fun Context.isTablet() = resources.isTablet()
With the above extension functions in place the implementation inside AWUtils would be simplified to
val isTablet: Boolean
get() = appContext.isTablet()
Inside (or on a reference of) any class that implements Context, such as Application, Activity, Service etc., you can then simply call isTablet()
class SomeActivity : Activity() {
fun someFunction() {
if (isTablet()) {
// ...
}
}
}
And elsewhere where Context or Resources are available in some way, you can simply call resources.isTablet()
class SomeFragment : Fragment() {
fun someFunction() {
if (resources.isTablet()) {
// ...
}
}
}
Edit 3: I'm starting to be fed up with Hilt, what is supposed would have been created to simplify our life, only makes our programming life much more complicated.
Yeah, Hilt is focusing on constructor injection and can only do field injection out-of-the-box in very limited cases, afaik only inside Android classes annotated with #AndroidEntryPoint and inside the class extending the Application class when annotated with #HiltAndroidApp.
Docs for #AndroidEntryPoint say
Marks an Android component class to be setup for injection with the standard Hilt Dagger Android components. Currently, this supports activities, fragments, views, services, and broadcast receivers.
If you feel that you need a lot of field injection, because you are working with "static"-like objects in Kotlin, consider using Koin instead of Hilt for your next project.

How to refer to a field of a Class in other Class?

There is integer variable 'a' in Class A, and method
Class A {
var a = 0
fun setA(int: Int) {
a = int
}
}
I used it in Class B by
Class B {
var classA = A()
classA.setA(10)
}
Then I want to set a to 100 in another class, Class C
But If I declare classA as A() and classA.setA(100) in same method of B, this doesn't change value of a referred in class B.
How to globally change the value of a in one place so that it's the same for all other classes?
class A {
companion object {
var a: Int = 0
}
}
perhaps you're looking for a companion object ? this allows global access to the same instance of a
you can now do:
A.a = 5
or in your case :
fun changeValue(int: Int) {
a = int
}
and this value will be the same everywhere
if you're trying to read the value back:
var example = A.a
note how I'm not creating an instance of A anywhere, because using a companion object is the same as using static in java, basically meaning that you can treat this as if an instance already exists

Kotlin: How to access field from another class?

package example
class Apple {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
Class:
package example
class Store {
fun buy() {
val SIZE = Apple.APPLE_SIZE_KEY
}
}
Error:
'APPLE_SIZE_KEY' has private access in 'example.Apple'
But official documentation describes that if we do not specify any visibility modifier, public is used by default.
Why is above error coming?
What you are trying to do is accessing a value of a class that has no instance. Here are three solutions:
package example
object Apple {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
This way you do not need to instantiate anything because of the way objects work in Kotlin.
You could also just instantiate your class like this:
package example
class Store {
fun buy() {
val SIZE = Apple().APPLE_SIZE_KEY
}
}
In this solution you also have an object of Apple, but Apple is still declared as a class.
The third option is a companion object, which behaves like static variables in Java.
package example
class Apple {
companion object {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
}
If you want this to be a class level property instead of an instance level property, you can use a companion object:
class Apple {
companion object {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
}
fun useAppleKey() {
println(Apple.APPLE_SIZE_KEY)
}
What you currently have is an instance property, which you could use like this:
fun useInstanceProperty() {
val apple = Apple()
println(apple.APPLE_SIZE_KEY)
}

Accessing static extension function from another class in Kotlin?

Let's say we have the following extension function:
class Helper {
companion object {
fun Int.plus(value: String) = Integer.valueOf(value).plus(this)
}
}
How can you access the plus extension function from the Helper class in another class. Is there a way where we can do something like this for instance:
class OtherClass {
fun someMethod() {
val eight = 7.Helper.Companion.plus("1")
}
}
In your example Int.plus(value: String) is a member function of the Helper.Companion object (the fact that it is a companion object or that it is inside another class does not matter). This case is described in the Declaring Extensions as Members section of the documentation.
In short, to access a function with two receivers (an extension receiver of type Int and a dispatch receiver of type Helper.Companion) you have to have them both in the scope.
This can be achieved in a number of ways:
with(Helper.Companion) {
239.plus("")
}
or
with(Helper.Companion) {
with(239) {
plus("")
}
}
P.S. Putting an extension function into a companion object is very irregular and not idiomatic. I can hardly imagine why you would need that.
An extension declared like this is a member extension function, and is only going to be visible within the Helper class. If you need to access it outside of that class, put it in a wider scope, for example, make it a top level function. Alternatively, you could make it a regular function that takes two parameters, if you want to keep it within a class.
As an additional hint, you can mark this function an operator if you want to use it with the + symbol:
operator fun Int.plus(value: String) = Integer.valueOf(value) + this
val x = 2 + "25"

Property in interface cannot have a backing field

I am learning Kotlin. My code is as follows:
interface BaseLogicDecoupler<A : BaseViewNotifier, B : BaseScreenRouter> {
var notifier: A?
var router: B?
fun attachNotifier(notifier: A?) {
this.notifier = notifier
}
fun detachNotifier() {
notifier = null;
}
fun attachRouter(router: B?) {
this.router = router
}
fun detachRouter() {
router = null;
}
}
But when I change it and try to provide an accessor for property like following :
var notifier: A?
get() = notifier
It doesn't compile with error saying : Property in interface cannot have a backing field.
From the doc here, kotlin interfaces can provide implementation and can have properties with accessors. Why does the compilation fail?
I am unable to understand the error. What does it say? Can anyone explain in simple terms?
This is an unexpected corner case, kudos to you for finding it.
Let me briefly explain it what goes on. I will use a stripped interface A and class B for the sake of simplicity:
interface A {
var notifier: Int
}
Normally a var property in a class includes 3 components: a private backing field to store its value, a setter method to write to it and a getter method to read it. But an interface cannot have a field (because live is pain and some math does not add up if it does), so a var property in an interface includes only 2 components: a setter and a getter.
As I outlined above, our interface A has declared 2 methods: a setter and a getter, both without implementations. Let's add some implementations to it:
interface A2 {
var notifier: Int
get() {return 1}
set(v) {}
}
So far, so good. Two open methods with implementations, non of them uses any fields. But what if only one of the implementations is declared?
interface A3 {
var notifier: Int //ERROR: Property in an interface cannot have a backing field
get() {return 1}
//set(v) {}
}
It turns out that if you specify only a getter, Kotlin also generates a (default) setter for the property. In other words, A3 is similar to A4 here:
interface A4 {
var notifier: Int
get() {return 1}
set(v) {field = v} //Obviously an error
}
This may be a bug or an intended behaviour. Here is the issue ticket: https://youtrack.jetbrains.com/issue/KT-15193
Possible workarounds:
declare an adequate setter as in A2
use an abstract class instead of an interface

Categories

Resources