Property getter typed on Supertype instead of Implementation in Kotlin - 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
}

Related

Kotlin: Hashmap of interface methods by the implementing class name

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() }

smart class to val property inside the class

public val properties for interface does't do smart casting to the object that were set into them.
is there an alternative?
There is an option do declare 2 variables. one public interface and one private instance. but I prefer just one
what I wish to have:
class MyClass{
val myObservable: Observable<Unit> = PublishSubject.create<Unit>()
fun foo(){
myObservable.onNext(Unit) // smart casting doesn't work
}
}
what works but involves boilerplate code
class MyClass{
private val myPublisher = PublishSubject.create<Unit>()
val myObservable: Observable<Unit> = myPublisher
fun foo(){
myPublisher.onNext(Unit)
}
}

Kotlin lazy initialization in subclass

I'm trying to build a string with properties that are initialized in a subclass.
I read about lazy initialization but somehow this doesn't work as I expected.
abstract class SubProcessFullNameBuilder(technicalDomain: TechnicalDomainEnumeration) {
protected val moduleName = "td.${technicalDomain.value().toLowerCase()}.shared"
private val packageName by lazy { packageName() }
private val processName by lazy { processName() }
val processFullName: String = "$moduleName/$packageName.$processName"
protected abstract fun packageName(): String
protected abstract fun processName(): String
}
class WorkerFullNameBuilder(
private val jmsDirection: JmsDirectionEnumeration,
technicalDomain: TechnicalDomainEnumeration,
private val cdmCode: String) : SubProcessFullNameBuilder(technicalDomain) {
override fun packageName() = "$moduleName.workers.${jmsDirection.value().toLowerCase()}.${cdmCode.toLowerCase()}"
override fun processName() = "Worker"
}
Since I have overridden the packageName() and processName() properties, I would expect that on calling the packageName property it would use the implementation from the subclass.
But when I call the processFullName property, it throws a java.lang.NullPointerException.
val builder = WorkerFullNameBuilder(JmsDirectionEnumeration.ESB_IN, TechnicalDomainEnumeration.INFOR, "ccmd")
val name = builder.processFullName
How can I initialize the packageName and processName properties in a proper way?
This is a case of calling a non-final method in a constructor and thus accessing uninitialized variables.
This line is still evaluated eagerly, at the time when the base class is constructed:
val processFullName: String = "$moduleName/$packageName.$processName"
To get the values of the two lazy properties, this will make calls to the abstract methods, of which packageName() refers to jmsDirection and cdmCode to return its value - these properties are not initialized yet, because their values are set after the superclass constructor runs. Here's a simplified version of the subclass' constructor, decompiled back to Java:
public WorkerFullNameBuilder(#NotNull JmsDirectionEnumeration jmsDirection, #NotNull TechnicalDomainEnumeration technicalDomain, #NotNull String cdmCode) {
super(technicalDomain);
this.jmsDirection = jmsDirection;
this.cdmCode = cdmCode;
}
As a demonstration, if you don't refer to these, for example, if you return constants in both of the subclass methods, your code will actually run fine:
override fun packageName() = "foo"
override fun processName() = "Worker"
However, the solution you need here is most likely to make the processFullName property itself lazy instead of the two values it uses (which you're evaluating at constructor time right now anyway, so you're not making use of them being lazy). This means you don't even need those two as separate properties:
abstract class SubProcessFullNameBuilder(technicalDomain: TechnicalDomainEnumeration) {
protected val moduleName = "td.${technicalDomain.value().toLowerCase()}.shared"
val processFullName by lazy { "$moduleName/${packageName()}.${processName()}" }
protected abstract fun packageName(): String
protected abstract fun processName(): String
}

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
}

What is the correct way to add an abstract private getter and a public setter?

I have an interface where I want a property that can be modified innside the class, but not outside. I cannot use val because it needs to be mutable and the var keyword can not have a specified private setter since this is in an interface.
In Java I would have done this:
public <T> getMyProperty();
I can use the same approach in kotlin and just write the getter function dirrectly, but this does not seem like a kotlinlike approach. Is there a better way to achieve the same as this?
fun getMyProperty()
In Kotlin, you can actually override a val with a var, so, I think, what you want can be expressed as follows:
interface Iface {
val foo: Foo
}
class Impl : Iface {
override var foo: Foo
get() = TODO()
private set(value) { TODO() }
}
Or you can override the val with a property with a backing field and default accessors:
class ImplDefaultGetter : Iface {
override var foo: Foo = someFoo
private set
}
In both cases, the mutability and the presence of a private setter become an implementation detail of the classes and are not exposed through the interface.