I have ever used Java to programming in android, but a few weeks ago I started to learn kotlin, when I use Java I tried to use object oriented approach and use the less possible static objects or instances, so when I see some materials in internet about some implementations of consume web services in kotlin I see something like this:
/*call of method from activity*/
val message = WebServiceTask.getWebservice(getString(R.string.url_service))
/*Class to do the call to webservice*/
class WebServiceTask {
companion object {
fun getWebService(url: String): WebServiceResponse {
val call =
RetrofitInstance.getRetrofit(url).create(ApiService::class.java).getList()
.execute()
val webServiceResponse = call.body() as WebServiceResponse
return user
}
}
}
/*Class to get Retrofit instance*/
class RetrofitInstance {
companion object{
fun getRetrofit(url: String): Retrofit {
return Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
}
Like you see i use companion object in two classes and according to i read companion object is equivalent to static instance in java, so my question is:
is this code following object oriented programming?, this aproach is recommended?, in case that answer is no and which is the best implementation for this code in object oriented
Yes, companion object is Kotlin's equivalent of static members in Java. Everything that applies to static, applies to companion object as well.
The use of companion object depends on how it interacts with the state of class's object.
If you are using some methods which are totally Pure functions Or Some final values which you need to provide access to outside the class itself, in that case using companion object makes total sense.
It is recommended for the above conditions because it does not interfere with the state of the class's object.
So, for the given code snippet it is a valid use of companion object.
Observe that methods inside companion object do not interact with something which is not passed to them as parameters. Everything that you see is created/initialized or used inside the methods only, Just the result it gets out.
Note:
However, if your companion object members(values or functions) interfere with the state of the object, it will cause leaks, which will lead you to troubles you have never faced before.
Yes, it is equivalent to static. No, it is not recommended, as it leads to problems with mocking for testing, for example.
Related
I'm studying creation pattern(Singleton pattern) in android kotlin.
I have question about difference in making singleton object by companion object and object.
In some example, singletons are made like this.
class ABC {
companion object {
private var sInstance: ABC? = null
fun getInstance(): ABC {
if (sInstance == null) sInstance = ABC()
return sInstance ?: throw IllegalStateException("")
}
}
}
but with above method,
// a and b are not same object
val a = ABC()
val b = ABC.getInstance()
println(a == b) // false
but what I know, singleton in kotlin is just object.
So, my question is "Why, When use companion object to make singleton object"
getInstance() is just a "traditional" way of creating singletons. It is a common technique in e.g. Java, because contrary to Kotlin, Java doesn't have singletons provided by the language itself. For this reason you will see getInstance() from time to time in Kotlin as well. It may be the code ported from Java, it may be developed by a person who has more experience with Java, so they don't know there is a better alternative in Kotlin, etc.
Additionally, object is pretty much static. If we have more complicated logic for creating a singleton, it may be required to create it by some kind of factory (e.g. companion object).
So I would say, the rule of thumb is to use object as a default and only if this is not possible, create it manually. And in the latter case, it is more "Kotlin-ish" to use a property, not a getInstance() function.
I am using Companion object in service to expose my LiveData to a fragment. Is this okay to use or will it cause me problems like memory leaks?
In my service:
companion object {
val timeLeftInSeconds = MutableLiveData<Long>(0)}
In my fragment:
LockoutService.timeLeftInSeconds.observe(viewLifecycleOwner, Observer {...})
No it's fine because companion object is kinda like static fields, but I highly recommend to use a repository instead because it will increase you code readability and makes it more robust. Something like
object AppRepository{
val timeLeftInSeconds = MutableLiveData<Long>(0)}
}
And in fragment
AppRepository.timeLeftInSeconds.observe(viewLifecycleOwner
No it's totally alright because companion objects are like static properties in java and are not bound to the class you define them in.
Also you can put it in the same file, outside of your service
LockoutService.kt
val timeLeftInSeconds = MutableLiveData<Long>(0)}
class LockoutService {...}
And access it without mentioning the service name
To define a singleton, should I use Kotlin object declaration or to make an ordinary Kotlin class and inject it using dagger? In my opinion the first option is definitely easier but there may be a reason to use dagger in this situation that I'm not aware of.
Option 1 (notice object keyword):
object SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
Option 2 (notice class keyword):
class SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
#Module
class AppModule {
#Provides
#Singleton
internal fun provideTheUtil() = SomeUtil()
}
class MainActivity : BaseActivity() {
#Inject internal lateinit var util: SomeUtil
}
UPDATE 2019-07-03
#Blackbelt said in comments that we should prefer option 2 for testability. But libraries like MockK can mock objects too. So do you still think option 2 is the preferred one?
You might want to reconsider the need of NumberFormatUtil being a singleton. It might be cheaper if you use #Reusable with Dagger or even a factory without any scope directly.
If NumberFormatUtil is fairly simple and only provides a few utility methods, no state and no need for mocking in tests, you could use an object implementation, maybe using #JvmStatic for Java-inter-operability. But then you could go for global utility (extension) functions as well:
package xyz
fun formatNumber(number: Long) {
// ...
}
fun Long.format() = formatNumber(this)
You should use option 2.
In software engineering, the singleton pattern is a software design
pattern that restricts the instantiation of a class to one "single"
instance. This is useful when exactly one object is needed to
coordinate actions across the system.
From: Singleton Pattern
So, a singleton is single instance in a scope. In case of Android, it is virtual machine instance running the app. In case you need custom scopes, you have to use option 2 only.
But, if you have only static methods inside the object you want to inject its better to keep them as global methods and even get rid of object. No need to inject anything. It is similar to a java class with only static methods (I mentioned this point as it is a usual way of creating Utility classes).
However, if the object also has some state. I would recommend going dagger way. The object way does not provide with dependency injection. It only creates a Singleton. Your purpose for using dagger is dependency injection.
Like below?
val retro: Retro by lazy {
PilotApp.retro!!
}
class PilotApp : Application() {
companion object {
var retro: Retro? = null
}
override fun onCreate() {
retro = Retro(applicationContext)
super.onCreate()
}
}
Is this a good way of initialisation? Thanks in advance.
This is not following the rule of "Inversion of Control" and It is not a good idea.
The reason it is not a good idea is because whenever a class (like a ViewModel, Fragment or Activity or ...) wants to use the retro, they have to get the Retro object themselves by calling your first line (PilotApp.retro).
The alternative (called Dependency Injection/Inversion of Dependency) is that the Retro object is given to the class (again, the ViewModel or whatever) when it is initialized.
The reason why this is important is because with the second approach, you can make your classes that use the Retro, testable. You can give them RetroMock or TestRetro that does what you want (for example, mock an api to return an error).
Another note for your example, you don't need to make the retro nullable, you should make your var a lateinit and make it non-null.
I have just started messing around with the Kotlin programming language, which is pretty much cooler than Java. I have some doubts related to static methods and fields:
Q1: Official document says
Kotlin can also generate static methods for functions defined in named
objects or companion objects if you annotate those functions as
#JvmStatic.
But if you see below I can access bar() method as a static method, which works without using #JvmStatic annotation. But on official doc its throwing error -> Kotlin static method.
Class C{
companion object{
#JvmStatic
fun foo() { }
fun bar();
}
}
fun main(args: Array<String>) {
C.foo();
C.bar(); //this line works fine
}
Q2: Do I really need #JvmStatic and #JvmField to make things static?
As you can see with companion object, things are working as expected.
You can access members of a companion object as C.bar() in Kotlin, but not in Java. Without #JvmStatic, you would need to use C.Companion.bar() in Java, just as said in the docs.
Note that, without #JvmStatic, the function is compiled to an instance (non-static) method that is called on C.Companion in Java (and Kotlin simply shortens it to a call on C, but it's the same under the hood), so yes, you need either #JvmStatic and #JvmField to make a declaration in a companion object compile into a static member.
Basically, #JvmStatic and #JvmField are tools for Java interoperation that help with creating Java-friendly APIs, and if you don't need to call the Kotlin members from Java (e.g. they are internal to your Kotlin project, or you are developing a library that is unlikely to be used with Java), you can leave them as they are.
Yep, you do need #JvmStatic. The problem with your code that you call it in Kotlin while in documentation code was called in Java.
To be more precise, this code won't compile:
public void main(String[] args) {
C.foo(); // Ok
C.bar(); // Not ok
}
Kotlin knows about function in companion object so you can call it on class directly, while Java doesn't know anything about it. So you annotate any function with #JvmStatic and it becomes visible from Java code as static method.
And just to clarfiy - purpose of #JvmStatic is interop with Java code. If you write your application in Kotlin only you don't need #JvmStatic at all.
The documentation ("page 2") refers to the case where you call the function from Java, not Kotlin.
When you only use Kotlin, there is no need for the annotation. Instead use the way you declared your bar() method. So you can call C.bar()