what is the best way to hide an impl class behind an interface?
The companion creator works well, but needs to repeat all the arguments of the constructor twice.
Is there a way to simplify A.from(x,y,z,a,b,c,d) = AImpl(x,y,z,...)?
//the application will only see this interface
interface A {
val x: X
val y: Y
companion object {
//choose any implementation without breaking existing code
#JvmStatic
fun from(x: X, y: Y): A = AImpl(x = x, y = y) //inconvenient for many arguments
}
}
internal class AImpl(
override val x: X,
override val y: Y
) : A
fun someApplication() {
val a = A.from(..., ...) //no reference to AImpl
//use a: A
}
Thank you very much.
Martin
The way you wrote it is already good. If you're concerned about too many parameters, note that you don't have to name and pass every single one of them, and not every property needs to be passed as a constructor parameter. Depending on the implementation class, some properties can be defaulted or calculated:
interface A {
val x: X
val y: Y
companion object {
fun from(x: X, y: Y): A = AImpl(x)
}
}
internal class AImpl(
override val x: X,
) : A {
override val y = something()
}
Furthermore, AImpl doesn't have to be internal; it can be private or even an anonymous class, if it's a very short one (otherwise the code can become unreadable):
interface A {
val x: X
val y: Y
companion object {
fun from(x: X, y: Y): A = object : A {
override val x = x
override val y = y
}
}
}
Instead of using a member function of a companion object, a common Kotlin-idiomatic way is to use a standalone factory function with the same name as the interface:
fun A(x: X, y: Y): A = AImpl(x, y)
An example of such a function is MutableList: it has the same name as interface MutableList, and it happens to return an ArrayList but that's just implementation detail: it's only documented to return something that implements interface MutableList.
I don't really understand what are you tring to do.
So i'm not sure if this will be the answer to your question.
fun main() {
val x = X()
val y = Y()
val a = AImpl().from(x,y)
a.x.toString()
a.y.toString()
//use object class so you can use the class without ()
val b = BImpl.from(x,y)
b.x.toString()
b.y.toString()
}
class X{}
class Y{}
interface A
{
val x : X
val y : Y
fun from(xValue : X, yValue : Y) : A
}
internal class AImpl() : A {
override var x : X = X()
override var y : Y = Y()
override fun from(xValue : X, yValue : Y) : A {
x = xValue
y = yValue
return this
}
}
object BImpl : A {
override var x : X = X()
override var y : Y = Y()
override fun from(xValue : X, yValue : Y) : A {
x = xValue
y = yValue
return this
}
}
I don't think you should implementation anything in Interface
but If you want your code to work
companion object {
//choose any implementation without breaking existing code
#JvmStatic
//fun from(x: X, y: Y): A = AImpl(x = x, y = y)
//because you Return A That's why you got no reference to AImpl
//so don't return A but make it AImpl directly
//but it's still act like "A" interface.
fun from(x: X, y: Y) = AImpl(x,y)
}
}
Related
open class Parent(){
protected var z : Int? = null
private fun getMyAge(){
val x = 65
val y = 10
z = x % y
}
}
class Child:Parent(){
ovveride fun getMyAge()
println(z) //here I get null
}
My question is: why I get null?
Am I getting a variable from an inherited class incorrectly?
It's because when you override the function, the super function is not called. If you want the function in parent class called, you must change your code to this:
open class Parent(){
protected var z : Int? = null
private fun getMyAge(){
val x = 65
val y = 10
z = x % y
}
}
class Child:Parent(){
ovveride fun getMyAge()
super.getMyAge()
println(z)
}
I often have a class with properties that are initialized at instantiation, like
class C {
var x = 10
var y = v * 2 // v is some variable
}
val c = C()
then properties of c are changed, and later I need to re-initialize the properties (so that c.x is 10 again and c.y is v*2, where the value of v may have changed).
My current approach is to initialize the properties with dummy values (or alternatively use lateinit and type annotations), and assign the desired values in an extra function ini like
class C {
var x = 0
var y = 0
init {
ini()
}
fun ini() {
x = 10
y = v * 2
}
}
then I call c.ini() to re-initialize.
Is there a better (more succinct) way that avoids the dummy values?
Note that in JavaScript I can simply write
class C {
constructor() {
this.ini()
}
ini() {
this.x = 10
this.y = v * 2
}
}
What you are doing right now is totally fine. I wouldn't change it.
If you want to avoid the placeholder values of 0, or if the type of the property has no placeholder value that make sense, you can store the initial values in private lambdas:
class C {
private val xInit = { 10 }
private val yInit = { v * 2 }
var x = xInit()
var y = yInit()
fun reset() {
x = xInit()
y = yInit()
}
}
If you really want to reduce the boilerplate, I can only think of this rather hacky and slow solution that relies on reflection. This could be usable if the instances don't need to be reset that often, and you have lots of these classes with resettable properties, and you just hate writing boilerplate reset methods.
class Resettable<T>(val supplier: () -> T) {
var wrapped = supplier()
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return wrapped
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
wrapped = value
}
fun reset() {
wrapped = supplier()
}
}
fun <T: Any> T.reset() {
// look for properties delegated with Resettable
this::class.memberProperties.mapNotNull {
it.isAccessible = true
(it as KProperty1<T, *>).getDelegate(this)
}.filterIsInstance<Resettable<*>>().forEach {
it.reset()
}
}
Now you just need to do:
var v = 10
class C {
var x by Resettable { 10 }
var y by Resettable { v * 2 }
}
fun main() {
val x = C()
println(x.y) // 20
v = 20
x.reset()
println(x.y) // 40
v = 40
x.reset()
println(x.y) // 80
}
Note that the first reset call would load all the kotlin reflection classes, and would take a noticeable amount of time.
I have a complex object that I want to display in a textfield. This is working fine with a stringBinding. But I don't know how to make it two-way so that the textfield is editable.
package com.example.demo.view
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.*
class MainView : View("Hello TornadoFX") {
val complexThing: Int = 1
val complexProperty = SimpleObjectProperty<Int>(complexThing)
val complexString = complexProperty.stringBinding { complexProperty.toString() }
val plainString = "asdf"
val plainProperty = SimpleStringProperty(plainString)
override val root = vbox {
textfield(complexString)
label(plainProperty)
textfield(plainProperty)
}
}
When I run this, the plainString is editable and I see the label change because the edits are going back into the property.
How can I write a custom handler or what class do I need to use to make the stringBinding be read and write? I looked through a lot of the Property and binding documentation but did not see anything obvious.
Ta-Da
class Point(val x: Int, val y: Int) //You can put properties in constructor
class PointConverter: StringConverter<Point?>() {
override fun fromString(string: String?): Point? {
if(string.isNullOrBlank()) return null //Empty strings aren't valid
val xy = string.split(",", limit = 2) //Only using 2 coordinate values so max is 2
if(xy.size < 2) return null //Min values is also 2
val x = xy[0].trim().toIntOrNull() //Trim white space, try to convert
val y = xy[1].trim().toIntOrNull()
return if(x == null || y == null) null //If either conversion fails, count as invalid
else Point(x, y)
}
override fun toString(point: Point?): String {
return "${point?.x},${point?.y}"
}
}
class MainView : View("Hello TornadoFX") {
val point = Point(5, 6) //Probably doesn't need to be its own member
val pointProperty = SimpleObjectProperty<Point>(point)
val pc = PointConverter()
override val root = vbox {
label(pointProperty, converter = pc) //Avoid extra properties, put converter in construction
textfield(pointProperty, pc)
}
}
I made edits to your converter to "account" for invalid input by just returning null. This is just a simple band-aid solution that doesn't enforce correct input, but it does refuse to put bad values in your property.
This can probably be done more cleanly. I bet there is a way around the extra property. The example is fragile because it doesn't do input checking in the interest of keeping it simple. But it works to demonstrate the solution:
class Point(x: Int, y: Int) {
val x: Int = x
val y: Int = y
}
class PointConverter: StringConverter<Point?>() {
override fun fromString(string: String?): Point? {
val xy = string?.split(",")
return Point(xy[0].toInt(), xy[1].toInt())
}
override fun toString(point: Point?): String {
return "${point?.x},${point?.y}"
}
}
class MainView : View("Hello TornadoFX") {
val point = Point(5, 6)
val pointProperty = SimpleObjectProperty<Point>(point)
val pointDisplayProperty = SimpleStringProperty()
val pointStringProperty = SimpleStringProperty()
val pc = PointConverter()
init {
pointDisplayProperty.set(pc.toString(pointProperty.value))
pointStringProperty.set(pc.toString(pointProperty.value))
pointStringProperty.addListener { observable, oldValue, newValue ->
pointProperty.set(pc.fromString(newValue))
pointDisplayProperty.set(pc.toString(pointProperty.value))
}
}
override val root = vbox {
label(pointDisplayProperty)
textfield(pointStringProperty)
}
}
The following minimal example shows my problem quite well, I hope:
data class OneThing(val x: Int, val y: Int, val foo: String)
data class ASimilarThing(val x: Int, val y: Int, val bar: String)
fun <THING> process(thing: THING) {
println(thing.x)
println(thing.y)
}
fun main() {
val oneThing = OneThing(1, 2, "hi")
val aSimilarThing = ASimilarThing(3, 4, "ho")
process(oneThing)
process(aSimilarThing)
}
The two classes OneThing and ASimilarThing come from a library, so I can not change them. But semantically they are very similar, and I'd like to avoid implementing my logic two times, so I'd like to have it in a generic process function.
However the above does not compile. (In C++ it is possible.)
Is there a way to make process work? Ideally I'm looking for something like
fun <THING: [OneThing, ASimilarThing]> process(thing: THING) { ... }
But such a syntax does not exist.
Any other ideas? :)
Kotlin like Java is a statically typed, object oriented language. Therefore you'd have to provide an common supertype declaring the properties x and y.
As this is not the case you cannot achieve duck typing as you like. THING has a supertype of Any? and therefore x and y are not accessible.
You could work around your issue by using a wrapper or delegate:
sealed class ThingĀ {
abstract val x: Int
abstract val y: Int
}
class ASimilar(
delegate: orig.OneThing
): Thing() {
override val x get() = delegate.x
override val y get() = delegate.y
}
class ASimilarThing(
delegate: orig.ASimilarThing
): Thing() {
override val x get() = delegate.x
override val y get() = delegate.y
}
fun process(thing: Thing) {
println(thing.x)
println(thing.y)
}
I have a class Vec3i that extends Vec3t
data class Vec3i(
override var x: Int = 0,
override var y: Int = 0,
override var z: Int = 0
) : Vec3t(x, y, z)
that has as one secondary constructor as follow
constructor(v: Vec3t<Number>) : this(v.x.toInt(), v.y.toInt(), v.z.toInt())
and another class Vec3ub that extends always Vec3t
data class Vec3ub(
override var x: Ubyte = Ubyte(0),
override var y: Ubyte = Ubyte(0),
override var z: Ubyte = Ubyte(0)
) : Vec3t(x, y, z)
Where Vec3t is in turn
abstract class Vec3t<T : Number>(
override var x: T,
override var y: T,
open var z: T
) : Vec2t(x, y)
And Ubyte extends Number
I'd like to instantiate a Vec3i from a Vec3ub
Vec3i(vec3ub)
but the compilers complains that there is no constructor for that..
why isn't valid the secondary constructor I quoted previously?
For completeness, as stated in my comment, the following compiles correctly:
data class Vec3i(
override var x: Int = 0,
override var y: Int = 0,
override var z: Int = 0
) : Vec3t<Int>(x, y, z) {
constructor(v: Vec3t<out Number>) : this(v.x.toInt(), v.y.toInt(), v.z.toInt())
}
data class Vec3ub(
override var x: Ubyte,
override var y: Ubyte,
override var z: Ubyte
) : Vec3t<Ubyte>(x, y, z)
abstract class Vec3t<T>(
override var x: T,
override var y: T,
open var z: T
) : Vec2t<T>(x, y)
open class Vec2t<T>(
open var x: T,
open var y: T
)
fun test(vec3ub: Vec3ub) {
val vec3i = Vec3i(vec3ub)
}
abstract class Ubyte : Number()
Note the constructor(v : Vec3t<out Number>) : ... and all other added generic parameter types. Vec3t<out Number> is necessary instead of just Vec3t<Number> here since you don't pass a Number, but rather a subclass of it.