Kotlin: Hashmap of interface methods by the implementing class name - kotlin

I have a list of clases that implement a specific interface. The ability to construct those clases or not is not static (so it's not possible to use when(className)), and can be configured so I want to be able to create some clases or call some methods based on a hashMap of allowed "constructors". Then if the key identifying a class is in present in the hashmap I can call the corresponding method, otherwise I can safely ignore. Let me illustrate:
Let's say I have an interface like
interface Instanceable {
data class Config(
val bar: Whatever
)
fun getIntance(config: Config): Instanceable
}
Then I have several (let's say 10) classes that implement this interface
class Implementation1() : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private lateinit var foo: Whatever
override fun getIntance(config: Config) = Implementation1().also{ this#Implementation1.foo = config.bar }
}
I want to create a hashmap of the methods by the identifiers, so later down the lane I can grab the method from the hashMap by the key ID and just invoke() the value if it's there. Something like:
allowedInstances("INSTANCE_1")?.let{ it.invoke(someConfig) }
In order to do this I tried to create a hashMap of methods like this:
private val allowedInstances = mutableHashMapOf<String, Instanceable.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
But it fails with:
Type mismatch.
Required: Instanceable.(Instanceable.Config) → Instanceable
Found: KFunction2<Implementation1, Instanceable.Config, Instanceable>
If I create the hashmap directly and let the compiler infer the types like this:
private val allowedInstances = mutableHashMapOf(
Implementation1.ID to Implementation1::getIntance,
Implementation2.ID to Implementation2::getIntance,
Implementation4.ID to Implementation4::getIntance,
)
Checking the type of the hashmap shows:
HashMap<String, out KFunction2<Nothing, Instanceable.Config, Instanceable>>
In fact I can do:
private val allowedInstances = mutableHashMapOf<String, Nothing.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
So the actual question is:
Why the function of the second hashMap parameter has Nothing as the receptor? Why I cannot have the interface Instanceable instead?
Edit: Still not good to have the Nothing there:
allowedInstances["INSTANCE_1"]?.let{ it.invoke(Nothing, someConfig) }
//Fails with: Classifier 'Nothing' does not have a companion object, and thus must be initialized here
Edit 2: All of the errors are in compile time

Your function type
Instanceable.(Instanceable.Config) -> Instanceable
is describing an extension function on an instance of Instanceable. You need to omit the receiver from the function type to be able to match your constructors' signature:
(Instanceable.Config) -> Instanceable
Edit: The other half of the problem is that you define getInstance() as a member function of the class. So you have to create an invalid instance of your class to use to create a valid instance, which doesn't make sense.
I would delete the getInstance() function from your interface, and put the equivalent code in the constructor of your class. Then you can define a function type in your Map that constructs your items.
interface Instanceable {
data class Config(
val bar: Whatever
)
// REMOVE this: fun getIntance(config: Config): Instanceable
}
class Implementation1(config: Config) : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private val foo: Whatever = config.bar
}
private val allowedInstances = mutableHashMapOf<String, (Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = ::Implementation1
// and so on...
// If there's an implementation that has no config, you can use a lambda:
class NoConfigImplementation : Instanceable {
companion object {
const val ID = "INSTANCE_2"
}
}
allowedInstances[NoConfigImplementation.ID] = { _ -> NoConfigImplementation() }

Related

How to change return type based on a function input which is a class name?

I have multiple data classes and each class has a corresponding class containing more info. I want to write a function in which I should be able to pass an identifier (table name corresponding to the data class). Based on this identifier, object of the corresponding class should be made, the value changed and this object should be returned as output of the function. I have written a simplified version of it on playground but I am unable to get it to work. Any help is appreciated.
class someClass(
)
class objectForSomeClass(
var value: String
)
class someOtherClass(
)
class objectForSomeOtherClass(
var value: String
)
class doSomething() {
companion object {
val classMap = mapOf(
"someClass" to objectForSomeClass::class,
"someOtherClass" to objectForSomeOtherClass::class,
)
}
// Create a map of class name to a new object based on the class name input
fun dummyFun(className: String, valueInput: String): Map<String, kotlin.Any> {
var returnObject = mutableListOf<Pair<String, kotlin.Any>>()
when(className) {
"SOME_CLASS" -> {
returnObject = mutableListOf<Pair<String, justDoIt.classMap["someClass"]()>>()
}
"SOME_OTHER_CLASS" -> {
returnObject = Map<String, justDoIt.classMap["someOtherClass"]()>
}
}
returnObject[className].value = valueInput
return returnObject
}
}
fun main() {
var obj = doSomething()
var t = obj.dummyFun("SOME_CLASS", "Value to be inserted")
// do something with t
}
Not knowing more about your classes (the ones in your code are not data classes – a data class in Kotlin is a specific type of class) I still think a lot could be simplified down to maybe even this:
fun createObject(className: String, value: String): Any? {
return when (className) {
"SomeClass" -> ObjectForSomeClass(value)
"SomeOtherClass" -> ObjectForSomeOtherClass(value)
// ...
else -> null
}
}
Additionally:
The classMap is not necessary, you can hard-code the cases in the when clause as in my example. There is also no need for reflection, which you would need to create instances from SomeType::class.
With getting rid of classMap you also do not need the companion object holding it anymore, and then you are left with one function for creating instances of your classes, and this function does not have to be in a class. You might put it into a singleton class called object in Kotlin (https://kotlinlang.org/docs/object-declarations.html#object-expressions)
Data classes in Kotlin: https://kotlinlang.org/docs/data-classes.html
You could maybe also replace each class someClass & class objectForSomeClass pair with a class someClass with a companion object.

I have a question about generic in kotlin

I got an error. Like this :
Error 1 : Platform declaration clash: The following declarations have the same JVM signature (getData()Ljava/lang/Object;):
fun (): I defined in typeErasure2
fun getData(): I defined in typeErasure2
Error 2 : Platform declaration clash: The following declarations have the same JVM signature (getData()Ljava/lang/Object;):
fun (): I defined in typeErasure2
fun getData(): I defined in typeErasure2
fun main(args : Array<String>){
var te = typeErasure("Jennie")
println(te.getData())
var te2 = typeErasure2("Sam")
println(te2.getData())
}
class typeErasure<I>(name : I){
private val data : I = name
fun getData() : I = data
}
class typeErasure2<I>(name : I){
val data : I = name // error 1
fun getData() : I = data // error 2
}
when I use the private keyword the program can run, otherwise it will get an error. anyone can explain to me? :)
This has nothing to do with generics. The problem with your code is that
public fun getData(): I
Is an accesor for "data". But when "data" is a public field, then the accesor is redundant. So when you do:
val someValue = myObject.data
Then the compiler cannot tell if it should use the accessor getData() or it should just point to the field directly.
When getData is private, then the compiler clearly knows that it can't use it so then it will point to the field directly.
class typeErasure2<I>(name : I){
val data : I = name
fun getData() : I = data
}
fun main() {
val obj = typeErasure2<Int>(123)
println(obj.data) // <--- Ask yourself, what does this line do exactly?
}
class typeErasure2<I>(name : I){
val data : I = name
private fun getData() : I = data
}
fun main() {
val obj = typeErasure2<Int>(123)
println(obj.data) // <--- Here it is clear
// it is not the "getData" because that one is private,
// so it must be the public field "data" you are pointing to
}
Kotlin's logic of properties differ slightly from Java's fields.
Whenever you declare any variable in class, its getter and setters are automatically generated, and they can be customized with get() or set {} after it.
Declaring getVariable() manually will result in platform clash, as getter is already defined for the field in the variable declaration and you are creating function with the same name as well.
You can use #JvmField annotation to instruct the compiler to not generate any getter or setter for the field.
#JvmField
val data: I = name
fun getData(): I = data

How to make field required in kotlin DSL builders

In Kotlin, when creating a custom DSL, what is the best way to force filling required fields inside the builder's extension functions in compile time. E.g.:
person {
name = "John Doe" // this field needs to be set always, or compile error
age = 25
}
One way to force it is to set value in a function parameter instead of the body of the extension function.
person(name = "John Doe") {
age = 25
}
but that makes it a bit more unreadable if there are more required fields.
Is there any other way?
New type inference enables you to make a null-safe compile-time checked builder:
data class Person(val name: String, val age: Int?)
// Create a sealed builder class with all the properties that have default values
sealed class PersonBuilder {
var age: Int? = null // `null` can be a default value if the corresponding property of the data class is nullable
// For each property without default value create an interface with this property
interface Named {
var name: String
}
// Create a single private subclass of the sealed class
// Make this subclass implement all the interfaces corresponding to required properties
private class Impl : PersonBuilder(), Named {
override lateinit var name: String // implement required properties with `lateinit` keyword
}
companion object {
// Create a companion object function that returns new instance of the builder
operator fun invoke(): PersonBuilder = Impl()
}
}
// For each required property create an extension setter
fun PersonBuilder.name(name: String) {
contract {
// In the setter contract specify that after setter invocation the builder can be smart-casted to the corresponding interface type
returns() implies (this#name is PersonBuilder.Named)
}
// To set the property, you need to cast the builder to the type of the interface corresponding to the property
// The cast is safe since the only subclass of `sealed class PersonBuilder` implements all such interfaces
(this as PersonBuilder.Named).name = name
}
// Create an extension build function that can only be called on builders that can be smart-casted to all the interfaces corresponding to required properties
// If you forget to put any of these interface into where-clause compiler won't allow you to use corresponding property in the function body
fun <S> S.build(): Person where S : PersonBuilder, S : PersonBuilder.Named = Person(name, age)
Use case:
val builder = PersonBuilder() // creation of the builder via `invoke` operator looks like constructor call
builder.age = 25
// builder.build() // doesn't compile because of the receiver type mismatch (builder can't be smart-casted to `PersonBuilder.Named`)
builder.name("John Doe")
val john = builder.build() // compiles (builder is smart-casted to `PersonBuilder & PersonBuilder.Named`)
Now you can add a DSL function:
// Caller must call build() on the last line of the lambda
fun person(init: PersonBuilder.() -> Person) = PersonBuilder().init()
DSL use case:
person {
name("John Doe") // will not compile without this line
age = 25
build()
}
Finally, on JetBrains open day 2019 it was said that the Kotlin team researched contracts and tried to implement contracts that will allow creating safe DSL with required fields. Here is a talk recording in Russian. This feature isn't even an experimental one, so
maybe it will never be added to the language.
In case you're developing for Android I wrote a lightweight linter to verify mandatory DSL attributes.
To solve your use case you will only need to add an annotation #DSLMandatory to your name property setter and the linter will catch any place when it is not assigned and display an error:
#set:DSLMandatory
var name: String
You can take a look here:
https://github.com/hananrh/dslint/
Simple, throw an exception if it's not defined in your DLS after the block
fun person(block: (Person) -> Unit): Person {
val p = Person()
block(p)
if (p.name == null) {
// throw some exception
}
return p
}
Or if you want to enforce it at build time, just make it return something useless to the outer block if not defined, like null.
fun person(block: (Person) -> Unit): Person? {
val p = Person()
block(p)
if (p.name == null) {
return null
}
return p
}
I'm guessing your going off this example so maybe address would be the better example case:
fun Person.address(block: Address.() -> Unit) {
// city is required
var tempAddress = Address().apply(block)
if (tempAddress.city == null) {
// throw here
}
}
But what if we wanted to ensure everything was defined, but also wanted to let you do it in any order and break at compile time. Simple, have two types!
data class Person(var name: String = null,
var age: Int = null,
var address: Address = null)
data class PersonBuilder(var name: String? = null,
var age: Int? = null,
var address: Address? = null)
fun person(block: (PersonBuilder) -> Unit): Person {
val pb = PersonBuilder()
block(p)
val p = Person(pb.name, pb.age, pb.address)
return p
}
This way, you get to you the non-strict type to build, but it better be null-less by the end. This was a fun question, thanks.

How do I test generators for delegated properties?

In my current project there is a class that will later on be implemented by many others. This class provides some generators for delegated properties.
abstract class BaseClass {
protected val delegated1 get() = new Delegated1Impl()
protected val delegated2 get() = new Delegated2Impl()
...
}
This base class can be used this way:
class Example : BaseClass() {
var field1 by delegated1
var field2 by delegated2
}
Now I want to test these delegated generators. Some of them contain logic which I want to test, but for now I only want to know that everytime they are called they return a new instance.
Now my question is: how can I test these generators?
The generators are not visible outside of extending classes so I cannot simply create an instance of it and call these methods.
#Test
fun `delegated1 should always return a new instance`() {
val target = object: BaseClass()
val first = target.delegated1 // This does not work since it is protected
val second = target.delegated1
assertTrue(first !== second)
}
You need a new object created whenever you "call" the get method. So how to test it? With a provider
A Provider<T> is just an object that provides you new instances of a concrete class. Its signature is something like this:
interface Provider<T> {
fun get() : T
}
So you need to inject a new Provider<T> into your BaseClass:
abstract class BaseClass(
private val implementation1Provider : Provider<YourInterface>,
private val implementation2Provider : Provider<YourInterface>) {
protected val delegated1 get() = implementation1Provider.get()
protected val delegated2 get() = implementation2Provider.get()
...
}
Now you can inject your custom providers in the test and assert that they have been called:
#Test
fun `delegated1 should always return a new instance`() {
val implementation1Provider = ...
val target = Example(implementation1Provider, ...)
val first = target.field1
// assert that implementation1Provider.get() has been called
}

Property getter typed on Supertype instead of Implementation in Kotlin

Suppose I have two classes, a Baseand a Implwhich extends Base.
package mypackage
open class Base
class Impl : Base()
How would I create a private property for the concrete Impl-Type (for internal use), with a public getter typed as the Base-Type, to achieve polymorphism? My initial approach was like this:
class Test {
private val myType = Impl()
get():Base
}
However, the Kotlin compiler complains:
Error:(30, 11) Kotlin: Getter return type must be equal to the type of the property, i.e. 'mypackage.Impl'
Basically, this is what it would look like in plain Java:
public class Test {
private Impl myImpl = new Impl();
public Base getBase() {
return myImpl;
}
}
How could one achieve this? Am I missing something?
P.S. I am aware of Backing Fields and creating custom methods as a workaround for getter, I was just curious on how to approach this in an elegant, Kotlin style manner.
If the property is private, so will be the getter. In this case, it doesn't matter what type it will have. If you want to have a public property of base type, you'll need to declare it separately:
private val _myType = Impl()
public val myType : Base
get() = _myType
You would code this the same as you did in Java, using two different properties. Unless you are ok with Impl never being specialized in the class. So here are many options:
// if you don't need Impl typed as Impl then just hold it as base
class Test1 {
public val base: Base = Impl()
}
// have both with pointing one reference at the other
class Test2 {
private val _impl = Impl()
public val base: Base = _impl
}
// have both, second one is a getter (no real benefit over Test2)
class Test3 {
private val _impl = Impl()
public val base: Base
get() = _impl
}
// use a function to do basically a cast
class Test4 {
private val _impl = Impl()
public fun asBase(): Base = _impl
}
Or don't worry about this other property, any use of grabbing the Impl can hold it as type Base:
class Test5 {
public val impl: Impl = Impl()
}
// later
val thing: Base = Test5().impl
Maybe you are looking to build this in a way with a common interface to get the base implementation?
open class Base {}
// a common way to get the implementation from within a class
interface Based {
val base: Base
}
class ImplAbc : Base()
class ImplXyz : Base()
class TestAbc : Based {
override val base: Base = ImplAbc()
}
class TestXyz : Based {
private val _impl = ImplXyz()
override val base: Base = _impl
}