Cannot call a function from a child type from a constructor - kotlin

Is it possible to call a function from a child type from a constructor? Please take a look at the example
class Dog(animalType: DogType) : Animal(animalType) {
fun doSomething() {
animalType.runDogTypeFunction() // error but animalType is always DogType
}
}
abstract class Animal(val animalType: AnimalType)
interface AnimalType
enum class DogType() : AnimalType {
DOG1, DOG2;
fun runDogTypeFunction() {}
}
enum class CatType() : AnimalType {
CAT1, CAT2;
fun runCatTypeFunction() {}
}
animalType is const (val) so it always is DogType. I do not understand why I cannot call a method from the DogType class.
I tried to override val but I received NPE

Your property is declared in the base Animal class (as AnimalType). The constructor param in Dog doesn't exist by the time you call doSomething.
You could try something like this:
abstract class Animal<T: AnimalType>(val animalType: T)
class Dog(animalType: DogType) : Animal<DogType>(animalType) {
fun doSomething() {
animalType.runDogTypeFunction()
}
}

Related

Override generic method in abstract class with concrete type in subclass

I have an main class in Kotlin which defines one concrete and one abstract generic method as follows:
abstract class MainClass {
abstract fun <TParent, TChild> getChildren(parent: TParent): Array<TChild>?
fun <TParent, TChild> processChildren(parent: TParent) {
val children = getChildren<TParent, TChild>(parent)
// ... do something with children
}
}
As you can see the method is about a parent object of type TParent containing child objects of type TChild. The parent class and how to get the children from it should be defined by subclasses, which should provide the concrete types.
I did the following:
class MyClass : MainClass{
override fun getChildren(parent: MyParent): Array<MyChild>? {
//... some logic getting the children from a parent object
}
}
But that does not work because it won't compile ('getChildren' overrides nothing).
How can I define a generic abstract method and implement it with concrete types in a subclass? Note that it is important to me that the generic types are defined on method-level, not on class level!
You cannot do this. Due to the type erasure the methods signatures will not fit.
What you actually can do is to make this abstract method protected and overload it with some specific types
// protected abstract fun <TParent, TChild> getChildren...
class MyClass: MainClass() {
override fun <TParent, TChild> getChildren(parent: TParent): ArrayList<MyChild>? {
// Some dummy implementation
println(parent!!::class.java.canonicalName)
return null
}
fun getChildren(parent: MyParent): ArrayList<MyChild>? {
return getChildren<MyParent, MyChild>(parent)
}
fun getChildren(parent: MyParent2): ArrayList<MyChild2>? {
return getChildren<MyParent2, MyChild2>(parent)
}
}
But not really sure what is the sense of doing this especially if generic types are not bounded
Looks like you don't want the method in one class to process different types of parameters.
In this scenario, make the class generic:
abstract <TParent, TChild> class MainClass {
abstract fun getChildren(parent: TParent): Array<TChild>?
fun processChildren(parent: TParent) {
val children = getChildren<TParent, TChild>(parent)
// ... do something with children
}
}
class MyClass : MainClass<MyParent, MyChild>() {
override fun getChildren(parent: MyParent): Array<MyChild>? {
//... some logic getting the children from a parent object
}
}

How to call an abstract method from a Class parameter in Kotlin?

Aim
Have a function Book, which takes one of three Letter classes as argument myClass and then calls 'genericMethod()' from the abstract class which Letter*() has inherited.
Issue
If I try Book(LetterA()).read() I get the following error:
Type mismatch. Required: Class<SampleClassArguments.Alphabet> Found: SampleClassArguments.LetterA
Does Kotlin have any way to achieve this result?
Code
#Test
fun readBookTest() {
Book(LetterA()).read() /*<--error here*/
}
class Book(val myClass: Class<Alphabet>) {
fun read() {
val letterClass = myClass.getConstructor().newInstance()
letterClass.genericMethod(myClass.name)
}
}
class LetterA(): Alphabet()
class LetterB(): Alphabet()
class LetterC(): Alphabet()
abstract class Alphabet {
fun genericMethod(className: String) {
println("The class is: $className")
}
}
You need to define the Class type as covariant with the out keyword so any of the child classes is an acceptable argument:
class Book(val myClass: Class<out Alphabet>)
And when you use it, you need to pass the actual Class, not an instance of the class. You can get the Class by calling ::class.java on the name of the class:
#Test
fun readBookTest() {
Book(LetterA::class.java).read()
}

Type inference of class type parameter in abstract method

// Bars.kt
abstract class Bar
class BarToo(/* fields */) : Bar()
// Foos.kt
abstract class Foo<T : Bar> {
abstract fun foo(bar: T)
}
class FooToo : Foo<BarToo>() {
override fun foo(bar: BarToo) { /* */ }
}
// FoosBars.kt
private val foos = HashMap<String, Foo<out Bar>>()
fun <T : Foo<out Bar>> putFoo(name: String, foo: T) {
foos.putIfAbsent(name, foo)
}
fun doFoo(name: String, bar: Bar) {
val foo = foos[name] ?: return
// Error: Type mismatch: inferred type is Bar but Nothing was expected
// https://pl.kotl.in/TSp3eO_Tj
foo.foo(bar)
}
If I manually specify the bounds of T at the method's declaration, the error in doFoo is resolved, e.g.:
abstract class Foo /* ... */ {
abstract <T : Bar> fun foo(bar: T)
}
but obviously prevents the subclasses from using the type parameter from the class declaration.
Is this type of hierarchy possible in Kotlin, or should I better explain what I am trying to accomplish in order to avoid an XY problem?
Thanks!
You need to use in instead of out. This allows child classes to be used as follows:
foo.foo(bar)
foo.foo(BarToo()) // no compile error
Ref: this

Calling an overloaded method with the base class parameter type

Is it possible in Kotlin to call an overloaded method using the base class type as a parameter? This is best illustrated via an example
Base Sealed Class + Derived Classes
sealed class Event {
abstract val eventId: String
}
data class FirstEvent(
override val eventId: String
val first: String
) : Event()
data class SecondEvent(
override val eventId: String
val second: String
) : Event()
Utility Class having an overloaded method for each of the derived classes
class UtilityClass {
fun handle(event: FirstEvent) {
....
}
fun handle(event: SecondEvent) {
....
}
}
Is it possible to call methods of the utility class in such a way utility.handle(FirstEvent("id", "first) as Event) doing so is giving me the following exception
None of the following functions can be called with the arguments supplied.
you can do something like this
fun handleEvent(event: Event) {
when (event) {
is FirstEvent -> {
// event is automatically casted as FirstEvent
event.first
}
is SecondEvent -> ...
}
}

Get companion class in companion object

Is there a way to get the javaClass of the companion class inside a companion object without knowing it's name?
I suppose I could get it by doing something like this:
open class TestClass {
companion object {
init {
val clazz = Class.forName(this::class.java.canonicalName.removeSuffix(".Companion"))
}
}
}
However, this does not work for class InheritingClass : TestClass(). It would still give me TestClass, not InheritingClass.
I was hoping for something more straightforward like this::class.companionClass.
Getting the class of the companion object of a given class will look like this:
TestClass::class.companionObject
Here's an example:
class TestClass {
companion object {
fun sayHello() = "Hello world"
}
}
If you want to get the class that contains the companion, since the latter is always an inner class of the former,
class TestClass {
companion object {
fun whichIsMyParentClass() = this::class.java.declaringClass // It'll return TestClass
}
}
And to further simplify, you'll also want to create an extension property:
import kotlin.reflect.KClass
val <T : Any> KClass<T>.companionClass get() =
if (isCompanion)
this.java.declaringClass
else
null
So, whenever you want to get the parent class of the companion object,
class TestClass {
companion object {
fun whichIsMyParentClass() = this::class.companionClass // It'll return TestClass
}
}
The companion class itself has no reference to the actual class as you can see in this bytecode
public final class TestClass$Companion {
private TestClass$Companion() { // <init> //()V
<localVar:index=0 , name=this , desc=LTestClass$Companion;, sig=null, start=L1, end=L2>
L1 {
aload0 // reference to self
invokespecial java/lang/Object <init>(()V);
return
}
L2 {
}
}
public TestClass$Companion(kotlin.jvm.internal.DefaultConstructorMarker arg0) { // <init> //(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
<localVar:index=0 , name=this , desc=LTestClass$Companion;, sig=null, start=L1, end=L2>
<localVar:index=1 , name=$constructor_marker , desc=Lkotlin/jvm/internal/DefaultConstructorMarker;, sig=null, start=L1, end=L2>
L1 {
aload0 // reference to self
invokespecial TestClass$Companion <init>(()V);
return
}
L2 {
}
}
}
The reference is only the other way around (see decompiled kotlin class)
public final class TestClass {
public static final Companion companion = ...
}
So you can either do it as you just did by cutting off the .Companion part of the class name or you reference it by hard with TestClass::class.java (what is in my opinion no problem and the best solution)
If you need to print the class name, you can add simpleName, such as
this::class.java.declaringClass.simpleName