I have a test class that looks something like this
Behind the scenes, we're using Guice and DropwizardAwareModule to configure the binding and provide instantiated classes/beans.
class SomeTest {
companion object {
#RegisterExtension
#JvmField
val app = TestGuiceyAppExtension.forApp(MyServer::class.java)
.config(configFileName).hooks(GuiceyConfigurationHook {
builder(it)
}).create()
}
#Inject
#Named("something")
private lateinit var someClass: SomeClass
}
The SomeClass is defined in the following way:
class SomeClass(firstClass: FirstClass, secondClass: SecondClass) {
}
With no constructor injection.
In a separate DropwizardAwareModule, I define a Provides function that provides 2 possible instances of SomeClass. For example -
#Provides
#Named("something")
fun getSomeClass(firstClass: FirstClass, secondClass: SecondClass) {
return SomeClass(firstClass, secondClass)
}
The test class fails to inject SomeClass, claiming it doesn't have a no-args constructor, nor an #Inject annotated constructor, fair enough.
However, if I transform the field injection into a constructor injection, it works flawlessly.
That is - this configuration works well, and the SomeClass instance I provided in the module above, shows up alright.
class SomeTest #Inject constructor(#Named("something") someClass: SomeClass) {
}
Why is it so? is it because of some limitation in test classes? or is it something to do with #Provides generating an immutable instance, whereas field injection requires mutable objects?
Thanks!
Related
I'm implementing a library using KMM(Kotlin Multiplatform). And to create an instance of a class I used a static method. However, a static method's not accessible in iOS project. How can I implement it cleanly.
expect class Verifier{
companion object{
fun getInstance():Verifier
}
}
// actual implementations
actual class Verifier{
actual companion object{
actual fun getInstance():Verifier{
/// make an instance and return
}
}
}
And I would like to get an instance of Verifier class as the following:
// calling in swift
Verifier.getInstance()
// calling in kotlin
Verifier.getInstance()
Is it possible to access as above and implement static methods in a shared module.
Thank you!
I'm facing an issue, like if my repository injected was not a Singleton.
I have a repository (in reality many, but let's make it simple) marked as #Singleton
#Module
#InstallIn(SingletonComponent::class)
class AppModule {
#Provides
#Singleton
fun provideSettingsRepository(#ApplicationContext context: Context): SettingsRepository {
return SettingsRepositoryImpl(context)
}
}
Here the implementation of my repository :
class SettingsRepositoryImpl(context: Context) : SettingsRepository {
private var _flow = MutableStateFlow("init value")
override fun getFlow(): StateFlow<String?> = _flow.asStateFlow()
override fun setMyValue(value:String) {
_flow.value = value
}
}
When I use it apart of my service (in some viewModels or others class with DI), it work perfectly. Today I implemented an AIDL service and wanted to do some DI. I had to use field injection because the service constructor has to be empty. It seems like the update made from my application isen't reported on my "TestApplication" who consume the Service (like if I had two instance of my repository).
Here the code of my service :
#AndroidEntryPoint
class AppService : Service() {
#Inject lateinit var settingsRepository: SettingsRepository
fun someActionOnMyRepository() {
settingsRepository.setMyValue("whatever")
}
}
When I set the value from my UI (viewModel or any other class who as the repository injected), it's not updated in my service. The flow doesn't contains the new value (tested by debug or logcat).
I'm expecting my settingsRepository to be a singleton. What am I missing ? Is it because the field injection ?
Best regards
Ok, the problem was not from Hilt but about how i declared my service in my AndroidManisfest.xml
<service android:name=".services.MyAppService"
android:process=":remote" <----- THIS
android:exported="true">
Making it like that make it on another process. That mean it's like another app instance (so no more Singleton / SharedPreferences).
I'm a newbie in Kotlin. I want to ask what private constructor in Kotlin for? class DontCreateMe private constructor () { /*...*/ }. I mean what class is supposed to be if we can't create its instance?
Well, the answers in the comments are correct, but since nobody wrote a full answer. I'm going to have a go at it.
Having a private constructor does not necessarily mean that an object cannot be used by external code. It just means that the external code cannot directly use its constructors, so it has to get the instances through an exposed API in the class scope. Since this API is in the class scope, it has access to the private constructor.
The simplest example would be:
class ShyPerson private constructor() {
companion object {
fun goToParty() : ShyPerson {
return ShyPerson()
}
}
}
fun main(args: String) {
// outside code is not directly using the constructor
val person = ShyPerson.goToParty()
// Just so you can see that you have an instance allocated in memory
println(person)
}
The most common use case for this that I've seen is to implement the Singleton pattern, as stated by Mojtaba Haddadi, where the external code can only get access to one instance of the class.
A simple implementation would be:
class Unity private constructor() {
companion object {
private var INSTANCE : Unity? = null
// Note that the reason why I've returned nullable type here is
// because kotlin cannot smart-cast to a non-null type when dealing
// with mutable values (var), because it could have been set to null
// by another thread.
fun instance() : Unity? {
if (INSTANCE == null) {
INSTANCE = Unity()
}
return INSTANCE
}
}
}
fun main(args: Array<String>) {
val instance = Unity.instance()
println(instance)
}
This is often used so that classes that are resource consuming are only instantiated once or so that certain pieces of data are shared by the entire codebase.
Be aware that kotlin uses the object keyword to implement this pattern, with the advantage of being thread-safe. Also some developers consider Singletons to be an anti-pattern
Another use case for private constructors would be to implement Builder patterns, where classes that have complex initialization can be abstracted into a simpler API, so the user doesn't have to deal with clunky constructors. This other answer addresses its uses in kotlin.
One of the simplest uses in real life kotlin code that I've seen is on the Result implementation from the stdlib, where it's being used to change the internal representation of the object.
In Kotlin, a common use for object is using it for singletons, like:
object MyObject {
...
}
However, when using micronaut framework, the official documentation recommends using something like this:
#Singleton
class V8Engine : Engine {
override var cylinders = 8
override fun start(): String {
return "Starting V8"
}
}
Why can't I use simply object instead of using annotation #Singleton with a class?
With a #Singleton, Micronaut can automatically manage dependencies between beans. If you go with the other class in https://docs.micronaut.io/latest/guide/ioc.html#beans, translated to Kotlin:
#Singleton
class Vehicle(private val engine: Engine) {
public fun start() = engine.start()
}
It can't be just an object because takes a parameter.
This parameter is discovered by Micronaut to be the singleton instance of V8Engine, which needs that to be a #Singleton and not an object.
Of course, in this case you could just directly use V8Engine in Vehicle; but it's easier to change e.g. if you want Engine not to be a singleton anymore.
Why can't I use simply object instead of using annotation #Singleton
with a class?
You can use object instead of using #Singleton with a class. Micronaut won't manage instances for you, but that is allowed.
While i am reading document of Kotlin, i saw that we should avoid using open properties declared at base class:
It means that, by the time of the base class constructor execution, the properties declared or overridden in the derived class are not yet initialized. If any of those properties are used in the base class initialization logic (either directly or indirectly, through another overridden open member implementation), it may lead to incorrect behavior or a runtime failure. When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks.
The document said that properties in derived class are not yet initialized when base class's constructor is called. But, how can we access derived class's properties which are not initialized, from base class constructor(I assumed that the incorrect behavior or a runtime failure were caused by this situation)? Is it possible?
I don't know kotlin, but I'm assuming that open is the same as virtual in other languages. It is unsafe to call virtual members in a base class constructor because the base constructor is called before the derived constructor. If the overridden property requires that the derived class be fully initialized it can cause errors because the derived constructor has not yet been called when you are inside the base constructor. At least that is the way it works in .NET languages like C#.
Open functions in Kotlin are functions which can be overridden by a subclass. Generally, it's a good practice to limit a class's inheritance because you should provide a class with it's necessary codes to make it overridable. If your intention is not to let a class to override your base class, then you should make it final. So Kotlin make this easy by making each class and method final by default. You can find a more detailed answer in the Objects and Class chapter of the book Kotlin in Action.
The so-called fragile base class problem occurs when modifications of a base class
can cause incorrect behavior of subclasses because the changed code of the base class no
longer matches the assumptions in its subclasses. If the class doesn’t provide exact rules
for how it should be subclassed (which methods are supposed to be overridden and how),
the clients are at risk of overriding the methods in a way the author of the base class
didn’t expect. Because it’s impossible to analyze all the subclasses, the base class is
"fragile" in the sense that any change in it may lead to unexpected changes of behavior in
subclasses.
To protect against this problem, Effective Java by Joshua Bloch (Addison-Wesley,
2008), one of the best-known books on good Java programming style, recommends that
you "design and document for inheritance or else prohibit it." This means all classes and
methods that aren’t specifically intended to be overridden in subclasses need to be
explicitly marked as final .
Kotlin follows the same philosophy. Whereas Java’s classes and methods are open by
default, Kotlin’s are final by default.
I assume you are asking about this example in Kotlin documentation:
open class Base(val name: String) {
init { println("Initializing a base class") }
open val size: Int =
name.length.also { println("Initializing size in the base class: $it") }
}
class Derived(
name: String,
val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") }) {
init { println("Initializing a derived class") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in the derived class: $it")
}
}
Kotlin designers followed good practices learned, from other language mistakes, so they made class, properties, and functions closed by default for overriding or inheriting. why?
let's add the open modifier to the base class property and override it:
open class Base(open val name: String) {
init { println("Initializing a base class") }
open val size: Int =
name.length.also { println("Initializing size in the base class: $it") }
}
class Derived(
override val name: String,
val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") }) {
init { println("Initializing a derived class") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
}
fun main() {
println("Constructing the derived class(\"hello\", \"world\")")
Derived("hello", "world")
}
if you run this code the output will be like below:
Constructing the derived class("hello", "world")
Argument for the base class: Hello
Initializing a base class
**Exception in thread "main" java.lang.NullPointerException
at Base.<init> (File.kt:6)
at Derived.<init> (File.kt:12)
at FileKt.main (File.kt:23)**
The error is happening because this line of code
open val size: Int =
name.length.also { println("Initializing size in the base class: $it") }
Why? when we were trying to initialize the Derived class, first the superclass is initialized first, so the initialization is done by evaluating the super constructor argument, then the properties and init blocks in their declaration order in the class.
when it comes to val size: Int = name.length.also{...} the initialization calls the name property which is overridden by the Derived class, the one that does NOT yet initialize.
so by avoiding marking the base properties by open, you protect the base class client from abusing the class.