Kotlin adapter pattern: Duplicate method name error on getter function - kotlin

Just simple kotlin code to demo Adapter Pattern in Gang of Four Design Pattern. I have a presentation today about this but i can't done it. So sad. I don't want to add much details but they don't allow me to post without much details.
Exception:
Exception in thread "main" java.lang.ClassFormatError: Duplicate method name "getRadius" with signature "()I" in class file RoundHole
at java.lang.ClassLoader.defineClass1 (ClassLoader.java:-2)
at java.lang.ClassLoader.defineClass (ClassLoader.java:756)
at java.security.SecureClassLoader.defineClass (SecureClassLoader.java:142)
Code:
interface WorkingWithRound {
fun getRadius(): Int
}
open class RoundPeg(val radius: Int = 0): WorkingWithRound {
override fun getRadius() = radius
}
class RoundHole(val radius: Int = 0): WorkingWithRound {
override fun getRadius() = radius
fun fit(peg: RoundPeg) {
println(getRadius() >= peg.getRadius())
}
}
class SquarePeg(val width: Int = 0)
class SquarePegAdapter(val speg: SquarePeg): RoundPeg() {
override fun getRadius() = (speg.width / 2 * 1.4).toInt()
}
fun main() {
val hole = RoundHole(5)
val rpeg = RoundPeg(5)
hole.fit(rpeg)
val small_sqpeg = SquarePeg(5)
val large_sqpeg = SquarePeg(10)
//hole.fit(small_sqpeg) // this won't compile (incompatible types)
val small_sqpeg_adapter = SquarePegAdapter(small_sqpeg)
val large_sqpeg_adapter = SquarePegAdapter(large_sqpeg)
hole.fit(small_sqpeg_adapter) // true
hole.fit(large_sqpeg_adapter) // false
}

Kotlin generates getter method for instance variables, hence the error. Couple of options to fix the issue
Make radius variable private
open class RoundPeg(private val radius: Int = 0): WorkingWithRound
Mark radius with #JvmField to instruct compiler not to generate any getter
class RoundHole(#JvmField val radius: Int = 0): WorkingWithRound

I think this would be simpler (as well as avoiding the compilation problems) if the interface defined a property, rather than a getter method:
interface WorkingWithRound {
val radius: Int
}
That compiles down to pretty much the same bytecode; but the intent is clearer, and it can then be implemented directly in the constructors:
open class RoundPeg(override val radius: Int = 0): WorkingWithRound
class RoundHole(override val radius: Int = 0): WorkingWithRound {
fun fit(peg: RoundPeg) {
println(radius >= peg.radius)
}
}
The SquarePegAdapter can be simplified, as it no longer needs to store the speg value, but can simply use it as an initialiser for the property:
class SquarePegAdapter(speg: SquarePeg): RoundPeg() {
override val radius = (speg.width / 2 * 1.4).toInt()
}
And the rest of the code needs no changes.

Related

How to obtain extension properties by Kotlin reflection?

I have used the memberExtensionProperties() method, but result collection of the extension properties is empty. The test code is attached. What is the right procedure?
class ExtensionPropertyTest {
class DummyClass{}
val DummyClass.id get() = 99
val DummyClass.name get() = "Joe"
#Test
fun testExtensionProperties() {
val dummyClass = DummyClass()
expect(dummyClass.id).toEqual(99) // OK
val properties = DummyClass::class.memberExtensionProperties
.stream()
.toList()
expect(properties).toHaveSize(2) // Fails due a zero size
}
}
memberExtensionProperties does not return extensions over a class, but its members that are at the same time extensions:
fun main() {
println(DummyClass::class.memberExtensionProperties)
}
class DummyClass {
val String.foo: Int
get() = toInt()
}
It is not that easy if at all possible to find all extensions over a class, because extensions are detached from their receivers and they can be located anywhere in the classpath.

Kotlin: How to define a variable whose type depends on the input?

I have a function in Kotlin which takes a particular string as input. Depending on the input, I want to create a variable of a specific type and do some computations on it.
For example,
fun compute(input: String): Any{
if(input=="2d"){
var point: Point2D;// Points2D - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-1"){
var point: Point2DWithP1AsOrigin;// Point2DWithP1AsOrigin - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-2"){
var point: Point2DWithP2AsOrigin;
//initilize and do some computations
return point.findDistanceFromOrigin()
}
.
.
.
}
You can see in the above example, I want to initilize the type of point depending on the input and do computation and return.
All the if-else conditions have the same code except for the definition of the variable. How can I put all this in a single block with something like this:
var point: if(input=="2d) Point2D::class else if(input=="2d-1") Point2DWithP1AsOrigin::class.....
How can I do that?
You could do something like this
fun compute(input: String): Any{
val point: MyPoint = when(input) {
"2d" -> Point2D()
"2d-1" -> Point2DWithP1AsOrigin()
"2d-2" -> Point2DWithP2AsOrigin()
else -> Point2D() //fallback is necessary
}
//initilize and do some computations
return point.findDistanceFromOrigin()
}
But then it's essential that all those classes share the same interface. Because they need to have the same methods in order to do the same operations on them.
For example like this:
class Point2D : MyPoint {
override fun findDistanceFromOrigin() = 5
}
class Point2DWithP1AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 6
}
class Point2DWithP2AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 7
}
interface MyPoint {
fun findDistanceFromOrigin() : Int
}
You can store constructor references and then invoke required one
fun main() {
val constructors = mapOf(
"2d" to ::Point2D,
"2d-1" to ::Point2DWithP1AsOrigin,
"2d-2" to ::Point2DWithP2AsOrigin,
)
val type = "2d-2"
val constructor = constructors[type] ?: throw IllegalArgumentException("$type not supported")
val point = constructor()
println(point::class)
}
Output
class Point2DWithP2AsOrigin

private set(value) requires initilization before init{ }

I am trying to add a private set(value) to a var, but this requires to init the field before init { } has run. Why?
fun main(args: Array<String>) {
println("Hello fellow Stackoverflowers!")
val data = Data(listOf<Point>(Point(1, 1.0, 1.0), Point(2, 2.0, 2.0), Point(3, 3.0, 3.0)))
val testInit = TestInit(data)
testInit.magicMethod()
println("Relevant Point Id: ${testInit.relevantPoint.id}")
}
class TestInit(val someData: Data) {
var relevantPoint: Point // = Point(0,0.0,0.0) // or do this, but why? It is set in init{}, lateinit also not allowed
// private set // this works
private set(value) { // Why can't I do this? -> "Property must be initialized - Error"
if (value.id < 100)
field = value
else
field = someData.points.first()
}
init {
if (someData.points.size < 3) // doing validation before setting the point
throw IllegalArgumentException("Need at least three points!")
relevantPoint = someData.points.first() // here the point gets initialized
}
fun magicMethod() {
// do other calculations
relevantPoint = someData.points[someData.points.size / 2] // just assign some point
}
}
data class Data(var points: List<Point> = mutableListOf())
data class Point(val id: Int, val x: Double, val y: Double)
When you call relevantPoint = someData.points.first(), the field will only get initialised if it's id is less than 100 (due to your setter logic). So there is a chance that you won't have initialised the field.
You can either use a backing field if you need to keep the initialisation in init, or simply initialise the value inline, which won't go through the setter:
var relevantPoint = someData.points.first()
private set(value) {
...
}
Edit
It's interesting that this problem occurs even after you adding an else branch to your setter and I am not sure why that is. However for a solution (if you can't initialise it as above) you can easily use a backing field:
private var _relevantPoint: Point
var relevantPoint: Point
private set(value) {
if (value.id < 100) _relevantPoint = value
}
get() = _relevantPoint
init {
_relevantPoint = someData.points.first()
}
In Kotlin, variables must be either initialized on declaration or (only in case of var) get marked with lateinit.
According to the Documentation:
The modifier (lateinit) can be used on var properties declared inside the body of
a class (not in the primary constructor, and only when the property
does not have a custom getter or setter) as well as for top-level
properties and local variables.
So You can't do The latter
Your best bet is what Henry suggested:
var relevantPoint = someData.points.first()
private set(value) {
if (value.id < 100)
field = value
}
The other option is that you don't use custom setter and check your criteria elsewhere.
I think the best option is what was presented earlier.
But I thought of an alternative, this way.
data class Data(var points: List<Point> = mutableListOf())
data class Point(var id: Int, val x: Double, val y: Double)
class RelevantPoint(_point: Point) {
var point: Point = _point
set(value) {
if (value.id < 100)
field = value
}
}
fun main(args: Array<String>) {
println("Hello fellow Stackoverflowers!")
val data = Data(listOf<Point>(Point(1, 1.0, 1.0), Point(2, 2.0, 2.0), Point(3, 3.0, 3.0)))
val testInit = TestInit(data)
testInit.magicMethod()
println("Relevant Point Id: ${testInit.relevantPoint.point.id}")
}
class TestInit(val someData: Data) {
var relevantPoint: RelevantPoint
init {
if (someData.points.size < 3) // doing validation before setting the point
throw IllegalArgumentException("Need at least three points!")
relevantPoint = RelevantPoint(someData.points.first())
}
fun magicMethod() {
// do other calculations
relevantPoint.point = someData.points[someData.points.size / 2] // just assign some point
}
}

Kotlin overload Int operator in class for DSL statement

In next code I'm trying to write DSL, which allowed some constructions for hands of some robot.
It should have syntax below, which I should use in outer context for RobotBuilder class:
robot {
hands {
plastik width 3
load = light - medium
}
head {
...
}
etc...
}
The problem is in string load = light - medium. It's do nothing, but Robot created. What I do? Just overload minus operator for Int. For me when Kotlin compiler see expression of = - it should use overloaded operator. What I am doing wrong?
Should I create special class like LoadClassBuilder with only one attribute of type LoadClass and then overload minus operator in HandsBuilder for - and change types for load, light and medium or problem in outer context of my DSL?
class HandsBuilder {
var material: Material = Metal(0)
var minLoad: LoadClass = LoadClass.Medium
var maxLoad: LoadClass = LoadClass.Medium
val plastik: Plastik = Plastik(0)
val metal: Metal = Metal(0)
infix fun Metal.width(width: Int) {
material = Metal(width)
}
infix fun Plastik.width(width: Int) {
material = Plastik(width)
}
var load: Int = 0
val light: Int = 1
val medium: Int = 2
operator fun Int.minus(other: Int): Int {
println("minus call")
minLoad = LoadClass.values()[this]
println(this)
maxLoad = LoadClass.values()[other]
println(other)
return 1
}
fun build() = Hands(material, minLoad, maxLoad)
}
Update 1:
fun hands(init: HandsBuilder.() -> Unit) {
handsParamInRobotBuilderClass = HandsBuilder().apply(init).build()
}

(Interface, Class) Kotlin: Expecting member declaration

Class Aim: read review point and comment, ensure that point is within 0-5
class LimitedReview(val point:Int, val comment:String):Review {
if (point<0){
point=0
}
if (point>5){
point = 5
}
override fun stars(): Int =point
override fun info(): String =comment
}
interface Review{
fun stars():Int
fun info():String
}
Error:(2, 5) Kotlin: Expecting member declaration
Error:(2, 17) Kotlin: Conflicting overloads: public final fun (): Unit defined in LimitedReview, public final fun (): Unit defined in LimitedReview
Error:(2, 17) Kotlin: Function declaration must have a name
may i know how to change my code?
which topic should i learn to avoid the same error again?
Thanks!
The property you have in the primary constructor of the class will assign the value that you call the constructor with to the point property directly, when your class is created, and you can't modify it any more.
Basically, this code:
class LimitedReview(val point: Int)
Is the same as this:
class LimitedReview(point: Int) {
val point: Int = point // ctor param to property
}
If you want to perform logic before assigning the value to the property, you have to move the property outside the constructor, and initialize it manually.
This can be done in an initializer block, if you have complex logic for it:
class LimitedReview(point: Int) {
val point: Int
init {
if (point < 0) {
this.point = 0
} else if (point > 5) {
this.point = 5
} else {
this.point = point
}
}
}
Or if you can fit it into a single expression (coerceIn comes in handy here), then inline with the property declaration:
class LimitedReview(point: Int) {
val point: Int = point.coerceIn(0..5)
}