I am working with an abstract class and two concrete ones, which implementing the abstract one. The diagram is as the next:
My classes looks as:
abstract class NavItem() {
var attributes: String = ""
var text = ""
}
class NavMenu(val items: MutableList<NavItem>) : NavItem()
class NavLink(var shortText: String) : NavItem()
The problem is when I try to work with the items which could be NavMenu or NavLinks, the NavMenus has a collection of NavLinks.
I am trying to work with the items using polymorphism as the next code:
navMenu.items.forEach{ item ->
buildNavItem(item)
}
the buildNavItem methods seems as:
private fun buildNavItem(navMenu: NavMenu){
navMenu.items
navMenu.attributes
navMenu.items
}
private fun buildNavItem(navItem: NavItem){
navItem.text
navItem.attributes
}
private fun buildNavItem(navLink: NavLink){
navLink.text
navLink.attributes
}
But the code is always getting into buildNavItem(navItem: NavItem), even when in the for each I can see sometimes that the item is NavLink, or is NavMenu.
Any suggestion?
Thanks!!
That is not how polymorphism works. You have navMenu.items list that is the type of MutableList<NavItem>, it can store NavItems or its descendants. In forEach function you go through each item, which has NavItem type, and call buildNavItem(item) function. In this case buildNavItem(navItem: NavItem) is always called. To call the same method with another parameter you need to explicitly cast it to that type. What I recommend, and that's how polymorphism works, is to create buildNavItem() function in NavItem class and implement it in descendants:
abstract class NavItem() {
var attributes: String = ""
var text = ""
abstract fun buildNavItem()
}
class NavMenu(val items: MutableList<NavItem>) : NavItem() {
override fun buildNavItem() {
// ... your concrete implementation for NavMenu
}
}
class NavLink(var shortText: String) : NavItem() {
override fun buildNavItem() {
// ... your concrete implementation for NavLink
}
}
And then you can call it in forEach function:
navMenu.items.forEach { item ->
item.buildNavItem()
}
In that case buildNavItem() function will be called for the right object, that is stored in navMenu.items, i.e. if it is an object of NavLink type then function 'buildNavItem()', overridden in NavLink class, will be called.
Problem:
navMenu.items.forEach { item ->
item.buildNavItem()
}
Since items is of type List<NavMenu>, the compiler will call the function which is appropriate for an item of type NavMenu, in this case the overload which takes a NavMenu.
Solution:
In order to call a more specific overload the compiler needs to know the type. You can smart cast the items an the appropriate function will be called:
private fun buildNavItem(navMenu: NavMenu) {
when(navMenu){
is NavItem -> buildNavItem(navMenu) // navMenu is smart casted to NavItem
is NavLink -> buildNavItem(navMenu) // navMenu is smart casted to NavLink
else -> throw IllegalStateException("Unknown subtype ${navMenu::class.simpleName} of NavMenu")
}
}
This way whenever you call buildNavItem, you delegate to the appropriate function.
Related
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 am new to Kotlin and I was playing with it. I pretty much wanted to create a pretty basic event bus. So I came up with this
interface Event
interface EventListener<E : Event> {
fun handle(event: E)
}
interface EventBus {
fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>)
}
class MyBus() : EventBus {
private val eventListeners: MutableMap<String, MutableList<EventListener<out Event>>> = mutableMapOf()
constructor(listeners: List<Pair<Class<Event>, EventListener<Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val key = aClass.name
val listeners: MutableList<EventListener<out Event>> = eventListeners.getOrPut(key) { mutableListOf() }
listeners.add(eventListener)
}
}
val bus = MyBus(
listOf(
MyEvent::class.java to MyEventListener()
)
)
class MyEvent : Event
class AnotherEvent : Event
class MyEventListener : EventListener<MyEvent> {
override fun handle(event: MyEvent) {
}
}
what happens is that when I try to create MyBus using the constructor accepting the list of pairs, I get
Type inference failed. Expected type mismatch: inferred type is List<Pair<Class<MyEvent>,MyEventListener>> but List<Pair<Class<Event>,EventListener<Event>>> was expected
But if I change the constructor to be something like
constructor(listeners: List<Pair<Class<out Event>, EventListener<out Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
adding out pretty much everywhere, then the MyBus constructor works, but the invocation to registerListener(..) breaks for the same exact reason as before. So the only way to solve this is to add "out"s also on registerListener function.
I suspect I'm doing something wrong here, but I don't know what precisely. Any help?
If you want your EventListener to be able to consume Events, then its type has to be invariant or covariant (not declared out). If it let you pass your EventListener<MyEvent> as if it were an EventListener<Event>, then your MyBus class might call listener.handle(event) on it with some Event that is not a MyEvent, such as AnotherEvent. Then you will get a ClassCastException when it tries to cast this AnotherEvent to MyEvent.
To be able to store different types of invariant EventHandlers, you will have to remove the variance restrictions by using star projection, and cast them when you retrieve them from the map. So make the map keys into class objects instead of just Strings. Since you will not have the help of the compiler when working with the star-projected types, you need to be careful that you are only adding an item to your MutableMap that is of the same type as the Class key that's associated with it. Then when you retrieve items, only cast to an invariant type.
The other part of your issue is that your constructor needs a generic type. Right now it works exclusively with Event so it can't handle subtypes of Event. Kotlin doesn't (yet?) support generic types for constructors so you have to do this with a factory function.
Here's an example of all the above.
class MyBus() : EventBus {
private val eventListeners: MutableMap<Class<*>, MutableList<EventListener<*>>> = mutableMapOf()
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val listeners = retrieveListeners(aClass)
listeners.add(eventListener)
}
private fun <E: Event> retrieveListeners(aClass: Class<E>): MutableList<EventListener<E>> {
#Suppress("UNCHECKED_CAST")
return eventListeners.getOrPut(aClass) { mutableListOf() } as MutableList<EventListener<E>>
}
}
// Factory function
fun <E : Event> myBusOf(listeners: List<Pair<Class<E>, EventListener<E>>>): MyBus {
return MyBus().apply {
listeners.forEach {
registerListener(it.first, it.second)
}
}
}
And you might want to change the type of the factory parameter from a <List>Pair to a vararg Pair so it's easier to use.
Here's a stripped down example to explain the variance limitation.
Your interface for an Event consumer:
interface EventListener<E : Event> {
fun handle(event: E)
}
Two implementations of Event:
class HelloEvent: Event {
fun sayHello() = println("Hello world")
}
class BoringEvent: Event {}
A class implementing the interface:
class HelloEventListener: EventListener<HelloEvent> {
override fun handle(event: HelloEvent) {
event.sayHello()
}
}
Now you have an EventListener that can handle only HelloEvents. Try to treat it like an EventListener<Event>:
val eventListener: EventListener<Event> = HelloEventListener() // COMPILE ERROR!
Imagine the compiler did not prevent you from doing this and you do this:
val eventListener: EventListener<Event> = HelloEventListener()
eventListener.handle(BoringEvent()) // CLASS CAST EXCEPTION AT RUN TIME!
If this were allowed your HelloEventListener would try to call sayHello() on the BoringEvent, which doesn't have that function, so it will crash. This is what generics are here to protect you from.
Now suppose your HelloEventListener.handle() didn't call event.sayHello(). Well, then it could have safely handled a BoringEvent. But the compiler isn't doing that level of analysis for you. It just knows what you declared, that HelloEventListener cannot handle anything except HelloEvent.
I want to be able to create a custom builder-pattern DSL-type thing, and I want the ability to create new components in a clean and type-safe way. How can I hide the implementation details required for creating and extending such a builder-pattern?
The Kotlin docs give something like the following example:
html {
head {
title {+"XML encoding with Kotlin"}
}
body {
h1 {+"XML encoding with Kotlin"}
p {+"this format can be used as an alternative markup to XML"}
a(href = "http://kotlinlang.org") {+"Kotlin"}
// etc...
}
}
Here, all of the possible "elements" are predefined and implemented as functions that also return objects of the corresponding types. (eg. the html function returns an instance of the HTML class)
Each function is defined so that it adds itself to its parent context's object as a child.
Suppose someone wanted to create a new element type NewElem usable as newelem. They would have to do something cumbersome such as:
class NewElem : Element() {
// ...
}
fun Element.newelem(fn: NewElem.() -> Unit = {}): NewElem {
val e = NewElem()
e.fn()
this.addChild(e)
return e
}
every time.
Is there a clean way to hide this implementation detail?
I want to be able to create a new element by simply extending Element for example.
I do not want to use reflection if possible.
Possibilities I Tried
My main problem is coming up with a clean solution. I thought of a couple other approaches that didn't pan out.
1) Create new elements with a function call that returns a function to be used in the builder style such as:
// Pre-defined
fun createElement(...): (Element.() -> Unit) -> Element
// Created as
val newelem = createElement(...)
// Used as
body {
newelem {
p { +"newelem example" }
}
}
There are obvious downsides to this, and I don't see a clear way to implement it either - probably would involve reflection.
2) Override the invoke operator in companion object
abstract class Element {
companion object {
fun operator invoke(build: Element.() -> Unit): Element {
val e = create()
e.build()
return e
}
abstract fun create(): Element
}
}
// And then you could do
class NewElem : Element() {
companion object {
override fun create(): Element {
return NewElem()
}
}
}
Body {
NewElem {
P { text = "NewElem example" }
}
}
It is unfortunately not possible to enforce "static" functions to be implemented by subclasses in a type-safe way.
Also, companion objects aren't inherited, so the invoke on subclasses wouldn't work anyway.
And we again run into problems about adding children elements to the correct context, so the builder doesn't actually build anything.
3) Override the invoke operator on Element types
abstract class Element {
operator fun invoke(build: Element.() -> Unit): Element {
this.build()
return this
}
}
class NewElem(val color: Int = 0) : Element()
Body() {
NewElem(color = 0xff0000) {
P("NewElem example")
}
}
This might have worked, except for when you immediately try to invoke on the object created by the constructor call, the compiler cannot tell that the lambda is for the "invoke" call and tries to pass it into the constructor.
This can be fixed by making something slightly less clean:
operator fun Element.minus(build: Element.() -> Unit): Element {
this.build()
return this
}
Body() - {
NewElem(color = 0xff0000) - {
P("NewElem example")
}
}
But yet again, adding children elements to the parent elements isn't actually possible without reflection or something similar, so the builder still doesn't actually build anything.
4) Calling add() for sub-elements
To try to fix the issue of the builder not actually building anything, we could implement an add() function for sub-elements.
abstract class Element {
fun add(elem: Element) {
this.children.add(elem)
}
}
Body() - {
add(NewElem(color = 0xff0000) - {
add(P("NewElem red example"))
add(P("NewElem red example 2"))
})
add(NewElem(color = 0x0000ff) - {
add(P("NewElem blue example"))
})
}
But this is obviously not clean and is just deferring the cumbersome-ness to the usage side instead of the implementation side.
I think it's unavoidable to add some sort of a helper function for each Element subclass you create, but their implementation can be simplified with generic helper functions.
For example, you can create a function that performs the setup call and adds the new element to the parent, then you only have to call into this function and create an instance of your new element:
fun <T : Element> Element.nest(elem: T, fn: T.() -> Unit): T {
elem.fn()
this.addChild(elem)
return elem
}
fun Element.newElem(fn: NewElem.() -> Unit = {}): NewElem = nest(NewElem(), fn)
Alternatively, you could create that instance via reflection to simplify even further, but since you've stated you'd like to avoid it, this will probably seem unnecessary:
inline fun <reified T : Element> Element.createAndNest(fn: T.() -> Unit): T {
val elem = T::class.constructors.first().call()
elem.fn()
this.addChild(elem)
return elem
}
fun Element.newElem(fn: NewElem.() -> Unit = {}) = createAndNest(fn)
These still leave you with having to declare a factory function with the appropriate header, but this is the only way to achieve the syntax that the HTML example achieves, where a NewElem can be created with its own newElem function.
I came up with a solution that isn't the most elegant, but it is passable and works the way I would want it to.
It turns out that if you override an operator (or create any extension function for that matter) inside a class, it has access to its parent context.
So I overrode the unary + operator
abstract class Element {
val children: ArrayList<Element> = ArrayList()
// Create lambda to add children
operator fun minus(build: ElementCollector.() -> Unit): Element {
val collector = ElementCollector()
collector.build()
children.addAll(collector.children)
return this
}
}
class ElementCollector {
val children: ArrayList<Element> = ArrayList()
// Add child with unary + prefix
operator fun Element.unaryPlus(): Element {
this#ElementCollector.children.add(this)
return this
}
}
// For consistency
operator fun Element.unaryPlus() = this
This allows me to create new elements and use them like this:
class Body : Element()
class NewElem : Element()
class Text(val t: String) : Element()
fun test() =
+Body() - {
+NewElem()
+NewElem() - {
+Text("text")
+Text("elements test")
+NewElem() - {
+Text("child of child of child")
}
+Text("it works!")
}
+NewElem()
}
I want to make a function extension for class inside of another class (for String in ModelAndView, not for inner), but have not found a way to do this without class inheritance. Is it possible at all?
Example of class extension:
class MyModelAndView : ModelAndView() {
infix fun String.to(value: Any?) {
addObject(this, value)
}
}
You can create member extension functions in classes, but these extensions can only be accessed in the class':
class X {
fun String.ext() = println("extension on $this called")
fun useExtension() {
val text: String = "myText"
text.ext()
}
}
You can only use this extension inside the class, as shown in useExtension, and also in the context of that class, which for instance can be used in with:
with(x) { "abc".ext() }
It's not recommended to do this, although it makes sense when writing DSLs for example.
Is there a way to specify the return type of a function to be the type of the called object?
e.g.
trait Foo {
fun bar(): <??> /* what to put here? */ {
return this
}
}
class FooClassA : Foo {
fun a() {}
}
class FooClassB : Foo {
fun b() {}
}
// this is the desired effect:
val a = FooClassA().bar() // should be of type FooClassA
a.a() // so this would work
val b = FooClassB().bar() // should be of type FooClassB
b.b() // so this would work
In effect, this would be roughly equivalent to instancetype in Objective-C or Self in Swift.
There's no language feature supporting this, but you can always use recursive generics (which is the pattern many libraries use):
// Define a recursive generic parameter Me
trait Foo<Me: Foo<Me>> {
fun bar(): Me {
// Here we have to cast, because the compiler does not know that Me is the same as this class
return this as Me
}
}
// In subclasses, pass itself to the superclass as an argument:
class FooClassA : Foo<FooClassA> {
fun a() {}
}
class FooClassB : Foo<FooClassB> {
fun b() {}
}
You can return something's own type with extension functions.
interface ExampleInterface
// Everything that implements ExampleInterface will have this method.
fun <T : ExampleInterface> T.doSomething(): T {
return this
}
class ClassA : ExampleInterface {
fun classASpecificMethod() {}
}
class ClassB : ExampleInterface {
fun classBSpecificMethod() {}
}
fun example() {
// doSomething() returns ClassA!
ClassA().doSomething().classASpecificMethod()
// doSomething() returns ClassB!
ClassB().doSomething().classBSpecificMethod()
}
You can use an extension method to achieve the "returns same type" effect. Here's a quick example that shows a base type with multiple type parameters and an extension method that takes a function which operates on an instance of said type:
public abstract class BuilderBase<A, B> {}
public fun <B : BuilderBase<*, *>> B.doIt(): B {
// Do something
return this
}
public class MyBuilder : BuilderBase<Int,String>() {}
public fun demo() {
val b : MyBuilder = MyBuilder().doIt()
}
Since extension methods are resolved statically (at least as of M12), you may need to have the extension delegate the actual implementation to its this should you need type-specific behaviors.
Recursive Type Bound
The pattern you have shown in the question is known as recursive type bound in the JVM world. A recursive type is one that includes a function that uses that type itself as a type for its parameter or its return value. In your example, you are using the same type for the return value by saying return this.
Example
Let's understand this with a simple and real example. We'll replace trait from your example with interface because trait is now deprecated in Kotlin. In this example, the interface VitaminSource returns different implementations of the sources of different vitamins.
In the following interface, you can see that its type parameter has itself as an upper bound. This is why it's known as recursive type bound:
VitaminSource.kt
interface VitaminSource<T: VitaminSource<T>> {
fun getSource(): T {
#Suppress("UNCHECKED_CAST")
return this as T
}
}
We suppress the UNCHECKED_CAST warning because the compiler can't possibly know whether we passed the same class name as a type argument.
Then we extend the interface with concrete implementations:
Carrot.kt
class Carrot : VitaminSource<Carrot> {
fun getVitaminA() = println("Vitamin A")
}
Banana.kt
class Banana : VitaminSource<Banana> {
fun getVitaminB() = println("Vitamin B")
}
While extending the classes, you must make sure to pass the same class to the interface otherwise you'll get ClassCastException at runtime:
class Banana : VitaminSource<Banana> // OK
class Banana : VitaminSource<Carrot> // No compiler error but exception at runtime
Test.kt
fun main() {
val carrot = Carrot().getSource()
carrot.getVitaminA()
val banana = Banana().getSource()
banana.getVitaminB()
}
That's it! Hope that helps.
Depending on the exact use case, scope functions can be a good alternative. For the builder pattern apply seems to be most useful because the context object is this and the result of the scope function is this as well.
Consider this example for a builder of List with a specialized builder subclass:
open class ListBuilder<E> {
// Return type does not matter, could also use Unit and not return anything
// But might be good to avoid that to not force users to use scope functions
fun add(element: E): ListBuilder<E> {
...
return this
}
fun buildList(): List<E> {
...
}
}
class EnhancedListBuilder<E>: ListBuilder<E>() {
fun addTwice(element: E): EnhancedListBuilder<E> {
addNTimes(element, 2)
return this
}
fun addNTimes(element: E, times: Int): EnhancedListBuilder<E> {
repeat(times) {
add(element)
}
return this
}
}
// Usage of builder:
val list = EnhancedListBuilder<String>().apply {
add("a") // Note: This would return only ListBuilder
addTwice("b")
addNTimes("c", 3)
}.buildList()
However, this only works if all methods have this as result. If one of the methods actually creates a new instance, then that instance would be discarded.
This is based on this answer to a similar question.
You can do it also via extension functions.
class Foo
fun <T: Foo>T.someFun(): T {
return this
}
Foo().someFun().someFun()