Let's say I have following class:
class Person() {
var age: Pair<String, Int> = Pair("person_age", 23)
// override getValue and setValue here
}
Now I want to capsulate the actual Pair and only want the user to read/write the second value of the pair. Is it possible to override the getValue and setValue methods so I can do something like this:
val p = Person()
p.age = 25
if(p.age <= 30)
Of course I can write own getter and setter methods for each property but one nice thing about Kotlin is that you have to write such less boilerplate code which will get lost then.
The following should probably already suffice:
class Person() {
var age : Int = 23 // public by default
private /* or internal */ fun toAgePair() = "person_age" to age // narrow visibility
}
So all your code accesses the age as you have shown:
val p = Person()
p.age = 25
if (p.age <= 30) ...
But if you require your Pair you just do the following instead:
p.toAgePair() // or skip that method and use: '"person_age" to p.age' instead
Alternatives to access the Pair content are: Pair.first, Pair.second or destructured, e.g.:
val myPair = Pair("person_age", 23)
// myPair.second = 25 // setting will not work however
myPair.let { (name, age) -> /* do something with it */ }
Or alternatively:
val p = Person()
val (name, age) = p.toAgePair()
// age = 25 // setting will not work however (and it wouldn't set the actual value inside the Pair if it would contain vars)
if (age < 30) // accessing is OK
However then you get access to both values which you probably didn't want in the first place, if I understood you correctly.
You could overcome the setting part using your own data class with a var but then again, you do not really gain something from it.
I wouldn't recommend you to use Pair at all. Maybe you could modify it (inherit from it, use extension functions) to suit your needs, but why try to change something as simple as Pair?. It is much easier and in this case also cleaner to just create your own class which suits your needs:
data class MyPair<out A, B>(
val first: A,
var second: B
)
val pair = MyPair("age", 1)
pair.second = 2
pair.first = 1 // error
This class has all important features which Pair has: generic types for first and second, and you can use destructuring declarations.
Now I want to capselate the actual Pair and only want the user to read/write the second value of the pair.
Assuming this means you want the first value to be final, but not the second one, there are some options.
If you only want one of the values to be writeable and readable, don't use a pair. It's not designed to be used like that. All the items of a Pair are vals.
If you want a Pair either way, can do this:
class Person(var age: Int = 23){
val pair: Pair<String, Int>
get() = Pair("person_age", age)
//Alternatively, if you don't want to use a property:
//fun getPair() = "person_age" to age
}
What this does is creating a final pair where the first value can't be modified, but the second can.
So now:
fun example(){
val person = Person()
person.age = 25;//Fine: Age is an int, and a var
//person.pair = Pair("something", 45)//fails: "Val cannot be reassigned
val pair = person.pair // Allowed. Accessing the pair still works
assert(pair.second == person.age) // This is true
}
However, if you're fine with a non-Pair solution, this works too:
data class Person (var age: Int, val string: String = "person_age")
fun example(){
val person = Person(23)
val (name, string) = person// Allowed! Just like with Pairs
person.age = 25; // Also allowed
//person.string = "something"//Not allowed
}
The n-touple unpacking is supported for data classes. If you don't have a data class, you need to declare an operator fun for each component you want to unpack. Example:
class Person (val string: String = "person_age", var age: Int){
operator fun component1() = string
operator fun component2() = age
}
But tbh, it sounds like the data class solution is the one you're looking for. It would lock the String to what it's initialized with, and because of the default value and its position, you can initialize it with a single positioned argument*
You could also use generics if you want to use the same class for multiple types.
* Assumes the code is in Kotlin. Positioned and default arguments don't work from Java code.
Here's how to overwrite a getter method in Kotlin
class Person {
var age: Int = 0
get() = if (field < 0) 0 else field
}
The attribute is accessed directly
fun main(args: Array<String>) {
val p = Person()
p.age = -28
println(p.age) //0
}
Related
I'm trying my hands on Kotlin. Being from a Python background is really giving me a tough time to get the knack of the Kotlin syntax. I'm trying to do a simple dictionary (Mutable Map) operation. However, its giving me exceptions.
This is what I tried. Kotlin compiler
Adding the code snippet for reference.
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied")){
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else{
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
I'm a bit late to the party, but I'd like to point out another solution here.
As I commented on the OP, heterogeneous maps with fixed string keys like this are usually better expressed with classes in Kotlin. For instance, in your case, the class for your main map's values could be the following:
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
(you could obviously add more properties if need be)
This would save you all the casts on the final values.
Another point I'd like to make is that if you access the value for a key anyway, you don't need to check up front the existence of the key with containsKey. Maps return null for keys that are not associated with any value (this is why you need to check for null after getting the value).
The compiler cannot see the correlation between containsKey and the subsequent get or [] access. However, it's smart enough to understand a null check if you simply get the value first and then check for null.
This always applies unless you want to tell the difference between keys that aren't in the map and keys that are in the map but associated null values (which is quite rare).
All in all, I would write something like that:
fun main() {
val openActivityMap = mutableMapOf<String, PackageInfo>()
val packageName = "amazon"
val currentTime = 23454321234
val packageInfo = openActivityMap[packageName]
if (packageInfo != null) { // the key was found and the value is smart cast to non-null in the next block
if (packageInfo.isAlreadyApplied) {
if ((packageInfo.lastAppliedAt - currentTime) > 3600) {
packageInfo.isAlreadyApplied = false
}
} else {
packageInfo.isAlreadyApplied = false
}
}
}
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
I would recommend writing tests first and working in small increments, but this should fix your compilation issues:
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean){
if((openActivityMap[packageName]?.get("lastAppliedAt") as Long - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else {
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
EDIT: Also I prefer to avoid nullable variables and mutable objects in general, but I suppose there's an exception to every rule.
Couldn't you just declare your Map<String, Any> to return a Boolean instead of Any? So,
val openActivityMap = mutableMapOf<String, MutableMap<String, Boolean>>()
It looks like you're trying to use your second Map to store both Booleans and Ints, which is complicating the logic. You'll need to typecast if you decide to approach it without Typing.
There's a problem with the 2 statement below
if(openActivityMap[packageName]?.get("isAlreadyApplied"))
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600)
As we all know, an IF statement requires a boolean value for it's param. The types of both statement are unknown at compilation time as they are of a Generic type, Any. As such,
openActivityMap[packageName]?.get("isAlreadyApplied") could be a null or of type Any (Not Boolean).
openActivityMap[packageName]?.get("lastAppliedAt") could be a null or of type Any (an Int was expected here for computation).
This would throw compilation errors as the compiler does not know the types to go with. What could be done is to cast to it's proper types.
Solution
openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean ?: false
((openActivityMap[packageName]?.get("lastAppliedAt") as Int ?: 0) - currentTime)
Giving a default value if it's null.
maybe you can try something like this
if (openActivityMap.containsKey(packageName)) {
val packageMap = openActivityMap[packageName]!!
val applyRequired = (packageMap["lastAppliedAt"] as Long - currentTime) > 3600
packageMap["isAlreadyApplied"] = packageMap.containsKey("isAlreadyApplied") && !applyRequired
}
btw. do you really want to have lastAppliedAt to be in te future? otherewise it will never be > 3600
first, I'm a kotlin neebie ^^.
I want to compare to objects from a data class. But the objects have variables that can be changed.
Is the code example a good practice to solve this or is there a problem that i can't see?
Ty
data class Test1(val id : Int, var name: FlexibleProperty<String>)
class FlexibleProperty<T>(var value: T) {
override fun equals(other: Any?) = true
override fun hashCode() = 1
}
fun main() {
val test1 = Test1(1, FlexibleProperty("Hans"))
val test2 = test1.copy()
println("test1 == test2 ${test1 == test2}")
println("test1 === test2 ${test1 === test2}")
test2.name = FlexibleProperty("Dieter")
println("test1 == test2 ${test1 == test2}")
println("test1 === test2 ${test1 === test2}")
}
EDIT:// Sry, I was a little confused ^^. My detailed problem is: I want to add these objects into a set. If I use normal string variables, the objects are different, so the set has 2 objects. But if I add test1 and check set.contains(test2) with my FlexiableProperty, the result is true, so I have to update the object. I don't want to check the id outside of the objects (with maybe a map and the id as key)
Here the code snippet with a set:
data class Test1(val id : Int, val name: FlexibleProperty<String>)
data class FlexibleProperty<T>(var value: T) {
override fun equals(other: Any?) = true
override fun hashCode() = 1
}
fun main() {
val test1 = Test1(1, FlexibleProperty("Hans"))
val test2 = test1.copy(name = FlexibleProperty("Dieter"))
val setTest = mutableSetOf(test1)
if (setTest.contains(test2)) {
setTest.remove(test1)
}
setTest.add(test2)
println("set $setTest")
}
There's no specific problem with your solution per see, but it could be greatly improved.
First, name can still be a value, since you use copy() anyway:
data class Test1(val id : Int, val name: FlexibleProperty<String>)
val test2 = test1.copy(name = FlexibleProperty("Dieter"))
Having no mutable properties make your class thread safe, and easier to reason about.
Second, when you use data class at the top level, it makes a lot of sense to make all classes it encapsulates also data classes. That would also solve your second problem with the need of overriding equals and hashCode:
data class FlexibleProperty<T>(var value: T)
Also, there's no reason to check referential equality with ===, at least with the examples you provide.
val cannot be reassigned compile time error var variable. Can't we change the array value?
Error
Array.kt:11:3: error: val cannot be reassigned
Code:
import java.util.Scanner
fun main(args: Array< String>){
println("Enter the no")
val scanner = Scanner(System.`in`)
var nos = Array<Int>(5){0}
var i : Int = 1
for (i in 1..3){
nos[i] = scanner.nextInt()
i = i+1
}
println("Given values $nos")
}
The for (i in 1..3) ... statement redefines i for the scope of its body, where it becomes a val (it's actually a separate variable that shadows the i declared outside the loop).
You can fix the code by using different names for these variables, or, in your case, by simply removing var i: Int = 1 and i = i + 1:
val scanner = Scanner(System.`in`)
var nos = Array<Int>(5) { 0 }
for (i in 1..3) {
nos[i] = scanner.nextInt()
}
println("Given values $nos")
UPD (answering to the comment): You can iterate in the opposite direction or using a non-unit step by building a progression with functions downTo and step, both described here in the reference.
var i : Int = 1
for (i in 1..3){
nos[i] = scanner.nextInt()
i = i+1
}
In this code you declared not one, but two variables with the name i because the for header creates its own declaration. Within the loop, only the version declared in the for header is visible, and that one is a val by definition.
Having said that, I'm unclear on what you were trying to achieve since everything looks like it would work just the way you want it without trying to update i in the loop.
I have come across the concept called destructuring declarations - when you can return multiple values from a function at once. It seems very convenient, but at the same time it looks like a tricky workaround. Each time when I think about that feature in Java, I understand that it's a hole in my architecture - there should probably be a class then, not just a couple of variables.
What do you think?
The concept allows having classes that clearly identify a few of their primary properties, the components.
Then you can access these components by using a destructuring declaration, without syntactic noise of accessing the properties.
Compare:
val point = clickEvent.getPointOnScreen()
val x = point.xCoordinate
val y = point.yCoordinate
// Use `x` and `y` in some calculations
and, assuming that the type has component1 and component2, just:
val (x, y) = clickEvent.getPointOnScreen()
Basically, it is not necessary to use this sort of syntactic sugar, and the concept itself does not harm any of the abstractions, it only provides a convenient way to access properties of a class instance in some cases when you don't need the instance itself.
Another example is working with map entries, e.g:
for ((key, value) in myMap) { /* ... */ }
There's still a Map.Entry<K, V> behind the (key, value) destructuring, and you can replace it by for (entry in myMap) ..., but usually it's the two properties that you need. This is where destructuring saves you from a little syntactic noise.
You can also define componentN function as extension for non data classes like this:
operator fun Location.component1() = latitude
operator fun Location.component2() = longitude
and when you want to process on list of locations, you can write this:
for ((lat, lon) in locations) {
......
}
What's the point of destructuring declarations in Kotlin?
Structuring, or construction, is creating an object from values in different variables. Destructuring is the opposite, to extract values into variables from within an existing object.
Part of the Kotlin philosophy is to be concise since the simpler and more concise the code is, the faster you’ll understand what’s going on. Destructuring improves readability which is part of being concise. Compare the following two snippets (let's consider the class Triple)
Without using destructuring
fun getFullName() = Triple("Thomas", "Alva", "Edison")
val result = getFullName()
val first = result.first
val middle = result.second
val last = result.third
Using destructuring
fun getFullName() = Triple("Thomas", "Alva", "Edison")
val (first, middle, last) = getFullName()
It is also possible to take advantage of destructuring to extract key and value from Map's entries.
for ((key, value) in aMap) {
/* ... */
}
Destructuring is the most useful when dealing with built-in data structures. Their fields have names making sense in the context of a data structure (handy when you're writing your own hashmap), but completely cryptic when you're dealing with the data contained there (which is 100% of the time, nobody writes their own hashmaps). Eg. Pair with it's first and second or Map.Entry with key and value.
Consider transforming Map values:
val myMap = mapOf("apples" to 0, "oranges" to 1, "bananas" to 2)
myMap
.asIterable()
.filter { it.value > 0 }
.sortedBy { it.key.length }
.joinToString(prefix = "We have ", postfix = " in the warehouse") {
"{$it.value} of ${it.key}"
}
To make it readable, you'd have to define intermediate variables:
myMap
.asIterable()
.filter {
val count = it.value
count > 0
}
.sortedBy {
val fruit = it.key
fruit.length
}
.joinToString(prefix = "We have ", postfix = " in the warehouse") {
val count = it.value
val fruit = it.key
"$count of $fruit"
}
Now it's readable, but at what cost?!?
Destructuring makes this cost more beareable:
myMap
.asIterable()
.filter { (fruit, count) -> count > 0 }
.sortedBy { (fruit, count) -> fruit.length }
.joinToString(prefix = "We have ", postfix = " in the warehouse") { (fruit, count) ->
"$count of $fruit"
}
That's the point.
I've read the docs on it 3 times and I still have no idea what it does. Can someone ELI5 (Explain Like I'm Five) it please? Here's how I'm using it:
fun main(args: Array<String>) {
val UserModel = UserModel()
val app = Javalin.create().port(7000).start()
with (app) {
get("/users") {
context -> context.json(UserModel)
}
}
}
with is used to access an object's members and methods without having to refer to the object once per access. It is (mostly) for abbreviating your code. It is frequently used when constructing an object:
// Verbose way, 204 characters:
var thing = Thingummy()
thing.component1 = something()
thing.component2 = somethingElse()
thing.component3 = constantValue
thing.component4 = foo()
thing.component5 = bar()
parent.children.add(thing)
thing.refcount = 1
// Terse way, 182 characters:
var thing = Thingummy()
with(thing) {
component1 = something()
component2 = somethingElse()
component3 = constantValue
component4 = foo()
component5 = bar()
parent.children.add(this)
refcount = 1
}
The documentation says:
inline fun <T, R> with(receiver: T, block: T.() -> R): R (source)
Calls the specified function block with the given receiver as its receiver and returns its result.
The way I think of it is that it is calling a function (the block) where this in the scope of the block is the receiver.
Whatever the block returns is the return type.
Essentially calling a method where you provide the implicit this and can return any result from it.
Here is an example to demonstrate:
val rec = "hello"
val returnedValue: Int = with(rec) {
println("$this is ${length}")
lastIndexOf("l")
}
The rec in this case is the receiver of the function call - the this in the scope of the block. The $length and lastIndexOf are both called on the receiver.
The return value can be seen to be an Int because that is the last method call in the body - that is the generic type parameter R of the signature.
The definition of with:
inline fun <T, R> with(receiver: T, block: T.() -> R): R (source)
Actually it's implementation is straight forward: The block is executed on receiver, which works for any type:
receiver.block() //that's the body of `with`
The great thing to mention here, is the parameter type T.() -> R:
It's called function literal with receiver. It's actually a lambda that can access the receiver's members without any additional qualifiers.
In your example the context of with receiver app is accessed in that way.
Besides stdlib functions like with or apply, this functionality is what makes Kotlin great for writing Domain Specific Languages as it allows the creation of scopes within which you have access on certain functionalities.
val citizen2 = Citizen("Tom", 24, "Washington")
val age = with(citizen2) {
println("$name - $age $residence ")
age = this.age + age
residence = "Florida"
age+10 // returns 58
}
println("${citizen2.name} - ${citizen2.age} - $age - ${citizen2.residence} ")
data class Citizen(var name: String, var age: Int, var residence: String)
Output:
Tom - 24 Washington
Tom - 48 - 58 - Florida
Note that :
We can access age property of citizen(receiver object) with this.age or age
last line(age+10 in this example) in the lambda of with() returns.
With is used to apply several operations to an object or access object's methods e.g. in this example we are accessing String's capitalize() extension method
data class Person(val name:String)
fun main(){
val person = Person("john doe")
with(person) {
println(name.capitalize()) // output John Doe
}
}
Under the hood with is a higher-order function. Here we are saying with the Person name call capitalize ( ). We don’t actually need ‘this’ because it is implicit and can be removed