Kotlin primary and secondary constructors during inheritance - kotlin

I am learning Kotlin and I'm kind of stuck with the constructors. I've written a program to help me understand the concept. The program is as follows:
open class Animal(){
var name :String = "noname";
constructor(name:String):this(){
this.name = name
}
open fun eat(){
println("$name the animal is eating")
}
}
class Dog: Animal{
constructor():super()
constructor(name : String):super(name)
override fun eat(){
println("$name the dog is eating")
}
}
fun main (args:Array<String>){
var a1 = Animal("foo")
var a2 = Animal()
var d1 = Dog("bar")
var d2 = Dog()
a1.eat()
a2.eat()
d1.eat()
d2.eat()
}
Is it possible to modify the program so that the child class calls the parent's primary constructor without using another secondary constructor.
What I'm trying to achieve here is to not force the user to pass a parameter while creating objects using primary and secondary constructors.
Please forgive me if my question is dumb. I am novice here.

In Kotlin, you can set the default value in the constructor itself:
open class Animal(var name: String = "noname"){
open fun eat(){
println("$name the animal is eating")
}
}
class Dog(name: String = "noname"): Animal(name){
override fun eat(){
println("$name the dog is eating")
}
}
This way, "noname" is assigned if no string is provided when invoking the constructor. Also, when you create a Dog object, it will always call the parent constructor.

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.

Problem with creating secondary constructor kotlin

Can someone explain me why I cannot create empty secondary construcor in my class?
I wanna TEST it but I need to create a instance of class to use the methods from, but my class need a parametr to create it. I thought to create a scecondary constructor but when I'm trying it makes a error "There's a cycle in the delegation calls chain". Excatly I wanna use it on this #TEST below but when I'm trying to create instance of Adapter class I must put there also (FragmentManager) inside. Any ideas?
class Adapter(sFM: FragmentManager) : FragmentPagerAdapter(sFM, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
constructor() : this()
private val pFragmentList = ArrayList<Fragment>()
private val pFragmentTitle = ArrayList<String>()
override fun getCount(): Int = pFragmentList.size
override fun getItem(position: Int): Fragment = pFragmentList[position]
override fun getPageTitle(position: Int): CharSequence = pFragmentTitle[position]
fun addFragment(fm: Fragment, title: String) {
pFragmentList.add(fm)
pFragmentTitle.add(title)
}
}
#Test
fun `create instance of class Adapter`() {
var adapter = Adapter().addFragment()
}
There is no FragmentPagerAdapter with empty constructor. Basically, what your code is trying to compile, is to do constructor that calls itself. If you want to use base class constructor you need to use super instead of this. But still, you won't find such constructor in base class. You always have to pass some FragmentManager

what is default value type of kotlin class constructor parameter?

class Greeter(name: String) {
fun greet() {
println("Hello, $name")
}
}
fun main(args: Array<String>) {
Greeter(args[0]).greet()
}
for above program I got this error
Unresolved reference: name
but when I add var or val
class Greeter(var name: String) {
or
class Greeter(val name: String) {
then program works fine, so why I need to add var or val to name, what is default type for constructor parameter val or var and why program gives me error when I not mention var or val
To use your value in the constructor like class Greeter(name: String), you can use init{}
class Greeter(name: String) {
var string:name = ""
init{
this.name = name
}
fun greet() {
println("Hello, $name")
}
}
or If you use val or var in the constructor it is more like class level variable and can be accessed anywhere inside the class
class Greeter(var name:String){
fun greet() {
println("Hello, $name")
}
}
The variable name can be used directly in the class then.
We can also give default values for the variables in both cases.
Adding val or var makes the parameter a property and can be accessed in the whole class.
Without this, it is only accessible inside init{}
The question is not making any sense, But the problem you are facing does make sense. In your case, the approach you are using is,
Wrong-Way:
// here name is just a dependency/value which will be used by the Greeter
// but since it is not assigned to any class members,
// it will not be accessible for member methods
class Greeter(name: String) {
fun greet(){} // can not access the 'name' value
}
Right-Way:
// here name is passed as a parameter but it is also made a class member
// with the same name, this class member will immutable as it is declared as 'val'
class Greeter(val name: String) {
fun greet(){} // can access the 'name' value
}
You can also replace val with var to make the name a mutable class member.

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
}

Searching a workaround for kotlin empty data class primary constructor

With given kotlin code :
sealed class Event(val id:String= UUID.randomUUID().toString(), val timestamp:Instant = Instant.now())
data class BarEvent(val additionalInfo:String):Event()
object FooEvent:Event()
// data class CorrectFooEvent():Event() // invalid kotlin
fun main(args: Array<String>) {
val b1 = BarEvent("b1")
val f1 = FooEvent
Thread.sleep(1000)
val b2 = BarEvent("b2")
val f2 = FooEvent
println("${b1.id} ${b1.timestamp} $b1")
println("${f1.id} ${f1.timestamp} $f1")
println("${b2.id} ${b2.timestamp} $b2")
println("${f2.id} ${f2.timestamp} $f2")
}
There is no issue with BarEvent.
But as FooEvent has no more parameter than the ones in Event, I would like it to have empty constructor. It's not authorized for data class, so I made it an object. But object is singleton, so it doesn't behave as an instanciated event.
The only workaround that I see (keeping the class as a data class) is something like :
sealed class Event(open val id:String= UUID.randomUUID().toString(), open val timestamp:Instant = Instant.now())
data class FooEvent(override val id:String= UUID.randomUUID().toString(), override val timestamp:Instant = Instant.now()):Event()
But it's not very elegant.
Just change FooEvent to a normal class, and add (or generate them using your IDE) toString(), hashCode() and equals(Object) if needed:
class FooEvent: Event() {
override hashCode() = ...
override equals(other: Object) {
...
}
override toString() = ...
}
To make the event a data class, simply add an unused property to it. Not pretty, but as short as it can be in Kotlin at the moment:
data class FooEvent(val dummy: Unit = Unit) : Event()
There seems to be no intention to remove this limitation soon:
Data class without arguments deprecated in 1.0. Why?
Suggestion for parameterless data class