Kotlin - Can't assign new values to lateinit vars via Destructuring Declaration - kotlin

The following Kotlin code works
val (x, y) = getSomeXYPair()
But you can't do this with lateinit global vars
class Foo {
private lateinit var x: Int
private lateinit var y: Int
init {
(x, y) = getSomeXYPair()
}
}
I'm just wondering if I'm either doing something wrong, or this just isn't supported in Kotlin?

You can't do it with any type of variables, local or not:
fun main() {
var x : Int = 1
var y : Int = 2
(x, y) = getSomeXYPair()
}
This syntax is simply invalid in Kotlin. You're required to start destructuring declaration with either var or val:
https://kotlinlang.org/docs/reference/multi-declarations.html#destructuring-declarations
That is, putting aside the fact that lateinit is not supported on primitive types such as Int.

Related

Operator overloading on += for set and get calls wrong setter

I have made an extension functions for BigIntegers, allowing me to add Ints to them.
operator fun BigInteger.plus(other: Int): BigInteger = this + other.toBigInteger()
// Allowing me to do
val c = myBigInt + 3
I have also made a Counter class, holding bigintegers for various keys, for easy counting. Since doing counter["1"] += myBigInt isn't allowed on standard maps (it's nullable), I have added a custom getter that returns a default value, making this possible.
class Counter<K>(val map: MutableMap<K, BigInteger>) : MutableMap<K, BigInteger> by map {
constructor() : this(mutableMapOf())
override operator fun get(key: K): BigInteger {
return map.getOrDefault(key, BigInteger.ZERO)
}
I can then use it like this
val counter = Counter<String>()
c["ones"] += 5.toBigInteger()
Problem is that I cannot use it like this:
c["ones"] += 5 // doesn't work, "Kotlin: No set method providing array access"
but this should be equivalent to this, which works, since it should use my extension operator on the bigint:
c["ones"] = c["ones"] + 5 // works
Why doesn't this work?
I've tried adding a set method for Ints, but then I see a very weird behavior. Kotlin will do the calculation correct, but then convert the BigInteger to an Int before passing it to my class! Example:
inline operator fun BigInteger.plus(other: Int): BigInteger {
val bigInteger = this + other.toBigInteger()
println("calculated bigint to $bigInteger")
return bigInteger
}
class Counter<K>(val map: MutableMap<K, BigInteger>) : MutableMap<K, BigInteger> by map {
constructor() : this(mutableMapOf())
override operator fun get(key: K): BigInteger {
return map.getOrDefault(key, BigInteger.ZERO)
}
operator fun set(key: K, value: Int) {
println("setting int $value")
map[key] = value.toBigInteger()
}
}
val c = Counter<String>()
c["1"] = "2192039569601".toBigInteger()
c["1"] += 5
println("result: ${c["1"]}")
c["1"] = "2192039569601".toBigInteger()
c["1"] = c["1"] + 5
println("result: ${c["1"]}")
Which prints
calculated bigint to 2192039569606
setting int 1606248646 <--- why does it call the int setter here?
result: 1606248646
calculated bigint to 2192039569606
result: 2192039569606
Why does Kotlin do the BigInt summation, but converts it back to an Int before sending to my setter?
Update
Since a comment suggest this is a compiler issue, any other ideas?
My ultimate goal here, was to have a counter of big integers, but to be able to easily add ints to it.
Adding this as a set function, makes it being called for both ints and bigints, so I can do the proper assignment myself. However, it will also then allow someone to add floats that will crash at runtime.
operator fun set(key: K, value: Number) {
map[key] = when (value) {
is BigInteger -> value
is Int -> value.toBigInteger()
else -> throw RuntimeException("only ints")
}
}
Any tips?
Notice that c["ones"] += 5 can be translated into calls in two ways:
c.set("ones", c.get("ones").plus(5))
c.get("ones").plusAssign(5)
The first way is what your code currently translates to, because you don't have a plusAssign operator defined. As I said in the comments, there is a bug in the compiler that prevents the operators from resolved correctly. When resolving c["ones"] += 5, It seems to be trying to find a set operator that takes an Int instead (possibly because 5 is an Int), which is unexpected. If you modify the code in the bug report a little, you can even make it throw an exception when executed!
class Foo {
operator fun get(i: Int) : A = A()
operator fun set(i: Int, a: A) {}
operator fun set(i: Int, a: Int) {}
}
class A {
operator fun plus(b: Int) = A()
}
class B
fun main(args: Array<String>) {
val foo = Foo()
foo[0] = foo[0] + 1
foo[0] += 1 // this compiles now, since there is a set(Int, Int) method
// but A can't be casted to Int, so ClassCastException!
}
It is rather coincidental (and lucky) in your case, that the compiler knows how to convert from BigInteger (or any other Number type actually) to Int, using Number#intValue. Otherwise the program would have crashed too.
A natural alternative way is to define the plusAssign operator, so that the assignment gets translated the second way. However, we can't do it on BigInteger, because plusAssign would need to mutate this, but BigInteger is immutable. This means that we need to create our own mutable wrapper. This does mean that you lose the nice immutability, but this is all I can think of.
fun main() {
val c = Counter<String>()
c.set("1", "2192039569601".toMutableBigInteger())
c.get("1").plusAssign(5)
println("result: ${c["1"]}")
}
data class MutableBigInteger(var bigInt: BigInteger) {
operator fun plusAssign(other: Int) {
bigInt += other.toBigInteger()
}
}
fun String.toMutableBigInteger() = MutableBigInteger(toBigInteger())
class Counter<K>(val map: MutableMap<K, MutableBigInteger>) : MutableMap<K, MutableBigInteger> by map{
constructor() : this(mutableMapOf())
override operator fun get(key: K): MutableBigInteger {
return map.getOrPut(key) { MutableBigInteger(BigInteger.ZERO) }
}
operator fun set(key: K, value: Int) {
println("setting int $value")
map[key] = MutableBigInteger(value.toBigInteger())
}
}
Notably, getOrDefault is changed to getOrPut - when a value is not found, we want to put the zero we return into the map, rather than just returning a zero that is not in the map. Our changes to that instance wouldn't be visible through the map otherwise.

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.

Destructuring Declarations in Kotlin

I want to return multiple values from a function. As suggested by another SO answer, I used Destructuring with public class, but the problem is I can't assign the returned destructured result to already existing variables.
data class Result(val res1 :Int, val res2: Int)
class test{
fun retresult():Result{
return Result(2,2)
}
}
fun main(args: Array<String>) {
var var1:Int = 0
var var2:Int = 0
var des = test()
//(var1, var2) = des.retresult() this doesn't work
var1 = des.retresult().res1
var2 = des.retresult().res2 // **works but calls function twice**
}
I don't want to initialize local vals at return point like
val (var1, var2) = des.retresult()
You can assign these two variables without calling the function twice, using with:
fun main(args: Array<String>) {
var var1:Int = 0
var var2:Int = 0
var des = test()
with (des.retresult()) {
var1 = res1
var2 = res2
}
}
Alternatively, your function could take function arguments for setting the results, and then you can pass the setters for these properties. This wouldn't work for local variables, only member properties. If you use C, this is kind of like passing a pointer to a function so it can directly modify a variable rather than returning something.
class Test (var one: Int, var two: Int)
fun doSomething(result1: (Int) -> Unit, result2: (Int) -> Unit) {
result1(2)
result2(2)
}
fun main() {
val test = Test(1, 1)
doSomething(test::one::set, test::two::set)
}
There's an open (and mostly forgotten, it seems) feature request for what you suggested, destructuring assignment to existing variables.

Initialize enum from associated value

I would like to initialize enum by its associated value.
My enum:
enum class DirectionSwiped(raw: Int){
LEFT(4),
RIGHT(8);
}
I would like to initialize it as such:
val direction = DirectionSwiped(raw: 4)
But I get this error:
Enum type cannot be instantiated
Why is this happening? In Swift, this functionality works like this:
enum Direction: Int {
case right = 2
}
let direction = Direction(rawValue: 2)
How can I make it work in Kotlin?
Yes you can
enum class DirectionSwiped(val raw: Int){
LEFT(4),
RIGHT(8);
}
val left = DirectionSwiped.LEFT
val right = DirectionSwiped.RIGHT
val leftRaw = DirectionSwiped.LEFT.raw
val rightRaw = DirectionSwiped.LEFT.raw
val fromRaw = DirectionSwiped.values().firstOrNull { it.raw == 5 }
This would be the correct way to access the instances of the enum class
What you are trying to do is create a new instance outside the definition site, which is not possible for enum or sealed classes, that's why the error says the constructor is private
As the error says, you cannot instantiate enums in Kotlin. A possible workaround would be to use a map and 2 helper methods to get enum values from raw values and vice versa:
enum class DirectionSwiped {
LEFT,
RIGHT;
fun toRaw() = enumToRaw[this]
companion object {
val rawToEnum = mapOf(
4 to LEFT,
8 to RIGHT
)
val enumToRaw = rawToEnum.entries.associate{(k,v)-> v to k}
fun ofRaw(raw: Int): DirectionSwiped? = rawToEnum[raw]
}
}
Usage:
val direction = DirectionSwiped.ofRaw(4) // LEFT
val raw = DirectionSwiped.LEFT.toRaw() // 4

Create instance in Kotlin and only set property if not null

I have a data class that looks like this:
data class MyDataCass(val x: String, val y: String,
val something: Something = Something(),
val somethingElse : SomethingElse = SomethingElse())
Since this class is going to be used from both Kotlin and Java I've also created a builder to make it easier to construct it from Java. The problem is that something and somethingElse are optional and I'd like to avoid duplication of the default values. This is essentially what I've tried:
data class MyDataCass(val x: String, val y: String,
val something: Something = Something(),
val somethingElse : SomethingElse = SomethingElse()) {
class Builder() {
private lateinit var x: String
private lateinit var y: String
private var something: Something? = null
private var somethingElse: SomethingElse? = null
// builder functions to set the properties defined above
...
fun build() = MyDataCass(x, y, something = ??, somethingElse = ??)
}
}
What I'd like to do is to set something and somethingElse only if they've been defined by the builder (i.e. they are not null), otherwise I'd like to use the default values as defined by the data class constructor. I could of course change the build function to look like this:
fun build() = if (something == null && somethingElse == null) {
MyDataCass(x, y)
} else if(somethingElse == null) {
MyDataCass(x, y, something = something)
} else {
MyDataClass(x,y, somethingElse = somethingElse)
}
}
but I find this to be quite verbose and error prone. Is there a less verbose or more idiomatic way of achieving this?
This is not supported very well by the language, if there was a good solution to this question that would help you too.
With that said, here's what I could come up with that lets you have the default values declared just once - at the cost of duplicating other code, however:
data class MyDataCass(val x: String, val y: String,
val something: Something,
val somethingElse: SomethingElse) {
companion object {
operator fun invoke(x: String, y: String,
something: Something? = null,
somethingElse: SomethingElse? = null) =
MyDataCass(x, y,
something ?: Something(),
somethingElse ?: SomethingElse())
}
class Builder {
private lateinit var x: String
private lateinit var y: String
private var something: Something? = null
private var somethingElse: SomethingElse? = null
// builder functions to set the properties defined above
// ...
fun build() = MyDataCass(x, y, something, somethingElse)
}
}
This way you can keep using the builder from Java, and in Kotlin the syntax that looks like is a call to the constructor is instead a call to the invoke function of the companion object, which has the default values. Your data class of course still has non-nullable types so that it's easier to use.