How to read properties as immutable strings in Kotlin - kotlin

I have been trying to read the read the properties in the Kotlin code. The lateinit var gets the work done but since it is mutable, the value can be changed even after initialisation. I want to read a property from a file and to not worry about it being changed anywhere in the file. I want something like lateinit val which is not present in Kotlin; or you somehow able to add #Value inside by lazy block of code.
I am working with AWS Secret Manager so I am putting the same code here but my doubt is more generic and not specific to AWS.
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
I tried doing the similar thing with by lazy:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(awsRegion)
.build()
}
The above codes are working fine but it would be much cleaner if there's a way to merge these 2 lines:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}

In your specific case you can inject property directly into bean method as an argument (method arguments are immutable)
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(
#Value("\${aws.secretsManager.region}") region: String
): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
or if you need this property in multiple #Beans you can inject it into constructor of enclosing configuration class
#Confiuration
class SomeConfig(
#Value("\${aws.secretsManager.region}")
private val region: String
) {
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
}

Related

Kotlin Abstract Val Is Null When Accessed In Init Before Override

In Kotlin, accessing an abstract val in an init block causes a NullPointerException since the field is overridden by an extending class after the super class's init block executes.
The ideal solution would be a way to declare some code/function to execute after all stages of object instantiation are complete. I can only think of creating an initialize() function and manually calling it, which is bad because it's not automatic. Sticking it in init block doesn't work as shown in the below example.
As a comment pointed out below, instead of overriding fields, they can be passed in as parameters, but that doesn't work for my actual use-case. It adds a lot of clutter for object construction and is a nightmare when other classes try to extend it.
Below example shows a solution using coroutines. Waiting for a field to != null works in this case, but doesn't not when map is an open val with a default value that may or may not get overridden.
The problem is somewhat solved, but the solution is far from optimal. Any suggestions and alternative solutions would be greatly appreciated.
#Test #Suppress("ControlFlowWithEmptyBody", "SENSELESS_COMPARISON")
fun abstractValAccessInInitNPE() {
val key = "Key"
val value = "Value"
abstract class Mapper {
abstract val map: HashMap<String, String>
fun initialize() { map[key] = value }
}
// Test coroutine solution on abstract mapper
println("CoroutineMapper")
abstract class CoroutineMapper: Mapper() {
init {
GlobalScope.launch {
while (map == null) {}
initialize()
}
}
}
val coroutineMapper = object : CoroutineMapper() {
override val map = HashMap<String, String>()
}
val start = System.nanoTime()
while (coroutineMapper.map.isEmpty()) {} // For some reason map == null doesn't work
println("Overhead: ${(System.nanoTime() - start) / 1000000.0} MS")
println("Mapped: ${coroutineMapper.map[key].equals(value)}")
// Test coroutine solution on open mapper
println("\nDefaultMapper")
open class DefaultMapper: Mapper() {
override val map = HashMap<String, String>()
}
val newMap = HashMap<String, String>()
val proof = "Proof"
newMap[proof] = proof
val defaultMapper = object: DefaultMapper() {
override val map = newMap
}
Thread.sleep(1000) // Definitely finished by the end of this
println("Mapped: ${defaultMapper.map[proof].equals(proof) && defaultMapper.map[key].equals(value)}")
// Basic solution (doesn't work)
println("\nBrokenMapper")
abstract class BrokenMapper: Mapper() {
init { initialize() } // Throws NPE because map gets overridden after this
}
val brokenMapper = object: BrokenMapper() {
override val map = HashMap<String, String>()
}
println("Mapped: ${brokenMapper.map[key].equals(value)}")
}
An open (as all abstract functions are) function should never be called from a constructor because then the class's initial state cannot be guaranteed in the superclass. It can lead to all kinds of very tricky bugs.
Usually there's a good way to design around this problem if you take a step back. For instance, instead of making the map an abstract property, make it a constructor parameter in the superclass. Then you know it's already initialized before subclass constructors can try to use it.
abstract class Mapper(key: String, value: String, val map: HashMap<String, String>)
abstract class DecentMapper(key: String, value: String, map: HashMap<String, String>) : Mapper(key, value, map) {
init {
map[key] = value
}
}
val key = "Key"
val value = "Value"
val decentMapper = object : DecentMapper(key, value, HashMap()){
//...
}

How can I access to data class object from activity?

I am trying to access to data class (Content)and I would like to use object(val isSelected: Boolean?)from PictureActivity. However, it causes UninitializedPropertyAccessException: lateinit property content has not been initialized. Do you know how to solve this situation? I used lateinit but I don't even know if using lateinit is the best way to access to data class(Content). If you know other way to access to it, please let me know.
The code is down below.
Content.kt
data class Content(
val id: Int,
val text: String,
val isSelected: Boolean?,
val url: String?
)
PictureActivity.kt
class PictureActivity : BaseActivity() {
private lateinit var binding: PictureActivityBinding
private lateinit var content: Content
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PictureActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
if(content.isSelected!!){
binding.button1.setOnClickListner{
startContentDownload(content.url!!)
return#setOnClickListener
}
}
private fun startContentDownload(url: String) {
//download image
}
}
}
lateinit keyword in Kotlin gives you an option to initialize it later but make sure you do it before you use.
To check if that variable is initialized or not, you can use below:
if(::content.isInitialized) {
// put your code here
}
In your case you have get data from somewhere(network call maybe) to fill in content data class, and then you will be able to use it.
You need to initialize the content variable first then only you can use it
content = Content(...)

MockK: mocking an jpa repository.save call

My code saves an object to database in some bigger method, but I don't need to test this.
So I want to mock the Repository.save method. But the save method returns the saved object.
I tried the following:
#MockK
private lateinit var mockJobRepository: JobRepository
val jobSlot = slot<Job>()
// ...
every { mockJobRepository.save<Job>(capture(jobSlot)) }
returns(jobSlot.captured)
But it throws an runtime error:
"lateinit property captured has not been initialized"
How do I just return the given argument in the mock?
Have you tried
private val mockJobRepository = mockk<JobRepository>()
?
I've notice #Mockk annotations on lateinit vars can be finicky
When using annotations, you have to tell Mockk at some point to initialize the annotated properties. Assuming you're using JUnit 5, you can do it by initializing mocks in #BeforeEach:
class Test {
#MockK
private lateinit var emailService: EmailService
#BeforeEach
fun setUp() {
MockKAnnotations.init(this)
}
}
...or just use the Mockk-Extension for JUnit:
#ExtendWith(MockKExtension::class)
class Test {
#MockK
private lateinit var emailService: EmailService
}
Btw. less verbose option than capturing the argument would be returnsArgument:
every { mockJobRepository.save<Job>(any()) } returnsArgument 0

Is there a cleaner way to set a top-level variable later in code without making it a lateinit var?

So what I want to achieve is that to have the top-level variable set some time later in the main function, but I don't want to make it a lateinit var which certainly breaks the Extension variable functionality.
For instance this code doesn't work since extension variables don't support lateinit modifier:
lateinit var Dispatchers.Konvironment: MainCoroutineDispatcher
private set
fun main() {
...
Dispatchers.Konvironment = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
So what I finally came up with is to use a dummy variable and implement the getter of the val variable.
val Dispatchers.Konvironment: MainCoroutineDispatcher
get() = dispatcher
private lateinit var dispatcher: MainCoroutineDispatcher
fun main() {
...
dispatcher = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
But it is certainly not clean way to do that. It looks ugly (ish) creating multiple variable in the top-level structure is not very clean architecture.
So is there any possible clean workarounds? Sort of like lazy initialization, by some delegates or something.
Well, partially answering your question:
var Dispatchers.Konvironment: MainCoroutineDispatcher
get() = dispatcher
private set(value) {
dispatcher = value
}
private lateinit var dispatcher: MainCoroutineDispatcher
fun main() {
...
Dispatchers.Konvironment = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
will give you the desired way of assigning the value. There is no way to get rid of this additional lazyinit variable, though.
Extensions are nothing more than just some Kotlin syntax sugar for static methods which take an instance of the extended class as one of the arguments, and perform some action. If you're familiar with Java then, for example, these extensions:
// Extensions.kt
fun Foo.extendedAction() {
println(this)
}
var Foo.extendedBar: Bar
get() = this.bar
set(value) {
this.bar = value
}
are under the hood these methods in Java:
public class ExtensionsKt {
public static final void extendedAction(Foo foo) {
System.out.println(foo);
}
public static final Bar getExtendedBar(Foo foo) {
return foo.getBar();
}
public static final Bar setExtendedBar(Foo foo, Bar bar) {
foo.setBar(bar);
}
}
The conclusion which maybe drawn from the above is that extensions don't actually add anything to the extended classes' signatures, they simply decorate them with additional functionality. Or, as put in the docs:
Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on variables of this type.
So you can see, unless dispatcher somehow already exists within Dispatchers, you can't do what you want without providing an external, "backing" variable which value can be actually referenced by the extension.

How can I set the JsName for a property's backing field in Kotlin?

I played about with Kotlin's unsupported JavaScript backend in 1.0.x and am now trying to migrate my toy project to 1.1.x. It's the barest bones of a single-page web app interfacing with PouchDB. To add data to PouchDB you need JavaScript objects with specific properties _id and _rev. They also need to not have any other properties beginning with _ because they're reserved by PouchDB.
Now, if I create a class like this, I can send instances to PouchDB.
class PouchDoc(
var _id: String
) {
var _rev: String? = null
}
However, if I do anything to make the properties virtual -- have them override an interface, or make the class open and create a subclass which overrides them -- the _id field name becomes mangled to something like _id_mmz446$_0 and so PouchDB rejects the object. If I apply #JsName("_id") to the property, that only affects the generated getter and setter -- it still leaves the backing field with a mangled name.
Also, for any virtual properties whose names don't begin with _, PouchDB will accept the object but it only stores the backing fields with their mangled names, not the nicely-named properties.
For now I can work around things by making them not virtual, I think. But I was thinking of sharing interfaces between PouchDoc and non-PouchDoc classes in Kotlin, and it seems I can't do that.
Any idea how I could make this work, or does it need a Kotlin language change?
I think your problem should be covered by https://youtrack.jetbrains.com/issue/KT-8127
Also, I've created some other related issues:
https://youtrack.jetbrains.com/issue/KT-17682
https://youtrack.jetbrains.com/issue/KT-17683
And right now You can use one of next solutions, IMO third is most lightweight.
interface PouchDoc1 {
var id: String
var _id: String
get() = id
set(v) { id = v}
var rev: String?
var _rev: String?
get() = rev
set(v) { rev = v}
}
class Impl1 : PouchDoc1 {
override var id = "id0"
override var rev: String? = "rev0"
}
interface PouchDoc2 {
var id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
}
class Impl2 : PouchDoc2 {
init {
id = "id1"
rev = "rev1"
}
}
external interface PouchDoc3 { // marker interface
}
var PouchDoc3.id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var PouchDoc3.rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
class Impl3 : PouchDoc3 {
init {
id = "id1"
rev = "rev1"
}
}
fun keys(a: Any) = js("Object").getOwnPropertyNames(a)
fun printKeys(a: Any) {
println(a::class.simpleName)
println(" instance keys: " + keys(a).toString())
println("__proto__ keys: " + keys(a.asDynamic().__proto__).toString())
println()
}
fun main(args: Array<String>) {
printKeys(Impl1())
printKeys(Impl2())
printKeys(Impl3())
}
I got a good answer from one of the JetBrains guys, Alexey Andreev, over on the JetBrains forum at https://discuss.kotlinlang.org/t/controlling-the-jsname-of-fields-for-pouchdb-interop/2531/. Before I describe that, I'll mention a further failed attempt at refining #bashor's answer.
Property delegates
I thought that #bashor's answer was crying out to use property delegates but I couldn't get that to work without infinite recursion.
class JSMapDelegate<T>(
val jsobject: dynamic
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return jsobject[property.name]
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
jsobject[property.name] = value
}
}
external interface PouchDoc4 {
var _id: String
var _rev: String
}
class Impl4() : PouchDoc4 {
override var _id: String by JSMapDelegate<String>(this)
override var _rev: String by JSMapDelegate<String>(this)
constructor(_id: String) : this() {
this._id = _id
}
}
The call within the delegate to jsobject[property.name] = value calls the set function for the property, which calls the delegate again ...
(Also, it turns out you can't put a delegate on a property in an interface, even though you can define a getter/setter pair which work just like a delegate, as #bashor's PouchDoc2 example shows.)
Using an external class
Alexey's answer on the Kotlin forums basically says, "You're mixing the business (with behaviour) and persistence (data only) layers: the right answer would be to explicitly serialise to/from JS but we don't provide that yet; as a workaround, use an external class." The point, I think, is that external classes don't turn into JavaScript which defines property getters/setters, because Kotlin doesn't let you define behaviour for external classes. Given that steer, I got the following to work, which does what I want.
external interface PouchDoc5 {
var _id: String
var _rev: String
}
external class Impl5 : PouchDoc5 {
override var _id: String
override var _rev: String
}
fun <T> create(): T = js("{ return {}; }")
fun Impl5(_id: String): Impl5 {
return create<Impl5>().apply {
this._id = _id
}
}
The output of keys for this is
null
instance keys: _id
__proto__ keys: toSource,toString,toLocaleString,valueOf,watch,unwatch,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__,__proto__,constructor
Creating external classes
Three notes about creating instances of external classes. First, Alexey said to write
fun <T> create(): T = js("{}")
but for me (with Kotlin 1.1) that turns into
function jsobject() {
}
whose return value is undefined. I think this might be a bug, because the official doc recommends the shorter form, too.
Second, you can't do this
fun Impl5(_id: String): Impl5 {
return (js("{}") as Impl5).apply {
this._id = _id
}
}
because that explicitly inserts a type-check for Impl5, which throws ReferenceError: Impl5 is not defined (in Firefox, at least). The generic function approach skips the type-check. I'm guessing that's not a bug, since Alexey recommended it, but it seems odd, so I'll ask him.
Lastly, you can mark create as inline, though you'll need to suppress a warning :-)