Actual function implementation returns KotlinCValue<AnyObject> instead of specific objective-c type - kotlin

I wan't to abstract two classes from their native implementation in my Kotlin Multiplatform project.
The two classes represent a Position and a Region but I also want to be able to transform them into their native objective-c implementation, so into CLLocationCoordinate2D and MKCoordinateRegion.
To achieve this I used the expect actual pattern but added the two functions toCoreLocation and toCoreRegion which should return me their native counterparts.
These functions work fine in the Kotlin code but the generated objective-c code does not return the type of CLLocationCoordinate2D or MKCoordinateRegion but just KotlinCValue<AnyObject>.
This is my commonMain code:
expect class Position(
latitude: Double,
longitude: Double
)
expect class Region(
center: Position,
latitudinalMeters: Double,
longitudinalMeters: Double
)
This is my actual implementation inside iosMain:
import platform.MapKit.MKCoordinateRegionMakeWithDistance
import platform.CoreLocation.CLLocationCoordinate2DMake
actual class Position actual constructor(
val latitude: Double,
val longitude: Double
) {
fun toCoreLocation() = CLLocationCoordinate2DMake(
latitude, longitude
)
}
actual class Region actual constructor(
val center: Position,
val latitudinalMeters: Double,
val longitudinalMeters: Double
) {
fun toCoreRegion() = MKCoordinateRegionMakeWithDistance(
center.toCoreLocation(), latitudinalMeters, longitudinalMeters
)
}
And this is the compiled objective-c code:
public class Position : KotlinBase {
public init(latitude: Double, longitude: Double)
open func toCoreLocation() -> KotlinCValue<AnyObject>
open var latitude: Double { get }
open var longitude: Double { get }
}
public class Region : KotlinBase {
public init(center: Position, latitudinalMeters: Double, longitudinalMeters: Double)
open func toCoreRegion() -> KotlinCValue<AnyObject>
open var center: Position { get }
open var latitudinalMeters: Double { get }
open var longitudinalMeters: Double { get }
}
Does anybody know how it's possible to return the native type from the compiled objective-c functions?
Do I maybe need to add .def file into the nativeInterop/cinterop directory?

Related

How to annotate lambda's return value

I have a function with lambda parameter:
private fun MyFun(
progress: () -> Float,
) {
// ...
}
I want to annotate return value of progress lambda with #FloatRange(from = 0.0, to = 1.0), but can't figure out how to do this. All my attempts to solve this causes syntax errors. Where am I wrong?
You can define a functional interface to describe your type, and annotate it there:
fun interface ProgressCallback {
#FloatRange(from = 0.0, to = 1.0) fun progress(): Float
}
private fun myFun(progress: ProgressCallback) {
// ...
}
fun foo() {
myFun { 5.0f } // error, doesn't conform to FloatRange
}
FYI, lint is not sophisticated enough to detect a failure to meet the requirement if you pass a function reference instead of a lambda, or if you indirectly call a function that returns a Float:
fun bar() = 5.0f
fun foo() {
myFun(::bar) // no error
myFun { bar() } // no error
}
Or if you return anything besides a single literal value:
fun foo() {
myFun { 1.0f * 2.0f } // no error
}
In kotlin language, lambda to perform a task, not to annotate. You can comment code likes this // FloatRange(from = 0.0, to = 1.0)
example :
// FloatRange(from = 0.0, to = 1.0)
private fun MyFun(
progress: () -> Float,
) {
}
you can read here to better understand lambda in kotlin language

Kotlin adapter pattern: Duplicate method name error on getter function

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.

Passing and Accessing Values and Functions of a Subtype that was passed into a Class Constructor requiring it's Supertype

I'll keep this as simple as possible. Let's say I have a parent class with a function as below that takes a position argument as a Point data class
open class Obj(val pos:Point) {
fun foo() : Double {
return 5.0 + pos.x * pos.y * pos.z
}
}
For sake of thoroughness, here is the Point data class
data class Point(val x:Double, val y:Double, val z:Double)
So I have multiple children that inherit from the Obj class but implement an additional function that is named the same in every child and calls a function in the parent,
class Cube(pos:Point) : Obj(pos) {
fun number() : Double {
return 10.0 * foo()
}
}
class Sphere(pos:Point) : Obj(pos) {
fun number() : Double {
return 20.0 * foo()
}
}
My question is, if I have a function somewhere that takes in objects that inherit from Obj but not Obj itself, how can I ensure they are of their own subtype rather than the Supertype?
For example, I currently have my function looking like this
fun foo2(obj:Obj) {
println(obj.number()) // throws error because obj doesn't have method number()
}
The normal approach to this sort of case would be to add an abstract method to the base class, and have the subclasses implement it. That way, whenever you have a base class reference, the compiler knows that method is available.
That would require the base class itself to be abstract, so you can't instantiate it directly. (And if foo() is only used by number() implementations, it might make sense to hide it from other classes.)
abstract class Obj(val pos: Point) {
abstract fun number(): Double
protected fun foo() = 5.0 + pos.x * pos.y * pos.z
}
class Cube(pos: Point) : Obj(pos) {
override fun number() = 10.0 * foo()
}
If, however, you need the base class to be instantiable, then that's more tricky: there's no easy way to specify 'only subclasses'. Depending on your exact requirements, you might allow the base class to be passed, and have it provide a default implementation of number():
open fun number() = foo()
Okay so using the suggestion from Animesh Sahu, I've implemented an Interface called ObjI in the base class, and required each implementation override the number() function. I combined that with the answer given by gidds, suggesting creating a function that calls another function. So the number() function in the base class just calls the foo() function
data class Point(val x:Double, val y:Double, val z:Double)
interface ObjI {
fun number() : Double
}
open class Obj(val p:Point) : ObjI {
override fun number() = foo()
fun foo() : Double {
return 5.0 + p.x * p.y * p.z
}
}
class Sphere(p:Point) : Obj(p) {
override fun number() : Double {
return 10.0 * super.foo()
}
}
class Cube(p:Point) : Obj(p) {
override fun number() : Double {
return 20.0 * super.foo()
}
}
fun main(args: Array<String>) {
val s = Sphere(Point(13.0, 6.0, 1.0))
val c = Cube(Point(13.0, 6.0, 1.0))
printem(s)
printem(c)
}
fun printem(o:Obj) {
println(o.number())
}

How to inherit operators in Kotlin?

I've the following two classes:
class Volume(var value: Double, unit: Unit) {
var unit: Unit = unit
private set
enum class Unit(symbol: String){
MILLILITER("ml"),
CENTILITER("cl"),
DECILITER("dl"),
LITER("l"),
TEASPOON("tsp"),
TABLESPOON("tbsp"),
FLUIDOUNCE("floz"),
SHOT("jig"),
GILL("gi"),
CUP("cup"),
PINT("pt"),
QUART("qt"),
GALLON("gal")
}
}
class Mass(var value: Double, unit: Unit) {
var unit: Unit = unit
private set
enum class Unit(symbol: String){
GRAM("g"),
DECAGRAM("dag"),
HECTOGRAM("hg"),
KILOGRAM("kg"),
OUNCE("oz"),
POUND("lb")
}
}
I want to create operators for both classes for basic arithmetic operations, for example:
operator fun inc(): Mass {
value++
return this
}
Since both classes will have the same operator logics, i don't want to duplicate this part of the code.
My first idea was that Both classes inherit from a PhysicalQuantity interface which contains the operators. In this case the following code doesn't work, because the IDE expects IPhysicalQuantity as return type but the type is Volume:
interface IPhysicalQuantity() {
var value: Double
var unit: IUnit
operator fun inc(): IPhysicalQuantity {
value++
return this
}
}
fun main() {
var vol = Volume(10.0, Volume.Unit.CENTILITER)
vol++
}
Same issue with abstract super class.
The problem with doing this inside the IPhysicalQuantity interface is that you don't want to return the object as the interface type IPhysicalQuantity from the inc method. Instead, you want to keep its original type (Volume or Mass), so you'd have to use generics there. However, I didn't find a way to do this without complex syntax and an unchecked cast:
interface IPhysicalQuantity<T : IPhysicalQuantity<T>> {
var value: Double
operator fun inc(): T {
value++
return this as T
}
}
class Volume(override var value: Double, unit: Unit): IPhysicalQuantity<Volume>
However, you can do this fairly simply with an extension instead without having to make the interface itself generic, if that works for you:
operator fun <T : IPhysicalQuantity> T.inc(): T {
value++
return this
}

How to use extension functions defined in Kotlin interface

In Kotlin, it is possible to declare an extension function in an interface like this:
interface Evaluator {
fun Double.evaluateY1(): Double
fun Double.evaluateY2(): Double
}
class EvaluatorImpl : Evaluator {
override fun Double.evaluateY1(): Double {
return this + 2.0
}
override fun Double.evaluateY2(): Double {
return this + 3.0
}
}
Having a receiver and an instance of the interface, how do I invoke such extension function?
I came up with a trick involving the with scope function, but I would prefer something with less indentation.
fun usageExample(evaluator: Evaluator, x: Double) {
with(evaluator) {
println("Y1 = ${x.evaluateY1()}. Y2 = ${x.evaluateY2()}")
}
}
There's no chance to make it work without getting into the scope of your interface. Using with is the recommended solution. You can use an expression body to make it more concise:
fun usageExample(evaluator: Evaluator, x: Double) = with(evaluator) {
println("Y1 = ${x.evaluateY1()}. Y2 = ${x.evaluateY2()}")
}