How do you initialize properties with custom setters that do error checking? - kotlin

How can I initialize a property with a custom setter?
For example, in my setter I make sure that the value is not negative.
class Foo(length: Int) {
var length = length
set(value) {
if (value < 0) throw IllegalArgumentException("Can't be negative.")
field = value
}
}
}
However if I call it as Foo(-5) the exception doesn't call since setters aren't called when properties are initialized.
Another thing I tried was below, but then it seems wrong to set the length twice.
class Foo(length: Int) {
var length = length
set(value) {
if (value < 0) throw IllegalArgumentException("Can't be negative.")
field = value
}
}
init {
this.length = length
}
}
Finally I thought about the code below, but that seems wrong since you are checking for the wrong value in two spots.
class Foo(length: Int) {
init {
if (length < 0) throw IllegalArgumentException("Can't be negative.")
}
var length = length
set(value) {
if (value < 0) throw IllegalArgumentException("Can't be negative.")
field = value
}
}
}

Your second method is just about as clean as you can make it, although I would set the initial value to some constant like 0 rather than length. Then if you forget to assign length in an init block, you get a compiler warning that the constructor parameter is unused.

There is also a 'fourth' way (variation of your second, actually):
class Foo {
var length = 0 // You cannot even assign length here
set(value) {
if (value < 0) throw IllegalArgumentException("Can't be negative.")
field = value
}
// Having to use a warning suppression is the primary drawback of this 'fourth' way
#Suppress("ConvertSecondaryConstructorToPrimary")
constructor(length: Int) {
this.length = length
}
}
Apart from the express warning suppression annotation, this way the cleanest in my opinion, with no repetition and with minimal boilerplate.

Related

Return Mono.empty() from Mono.fromCallable

I want to do something like below. In the Mono.fromCallable I run some block logic, then based on the value I either return Mono.empty() or the value so that it will either trigger the map or defaultIfEmpty.
Mono.fromCallable(() -> {
double number = Math.random();
if (number < 0.5) {
return Mono.empty();
}
return number;
})
.map(number -> 1)
.defaultIfEmpty(0)
This give an error since Mono.fromCallable expect a consistent return value. How do I adjust the code to make it work?
Although returning null is usually prohibited in Reactor APIs, it is a valid value that Callable may return, and Reactor handles it correctly by transforming into an empty Mono:
Mono.fromCallable(() -> {
double number = Math.random();
if (number < 0.5) {
return null;
}
return number;
})

Error "Out of segment space" in VMEmulator cause by a getter mwthod in Jack

I am doing a project for nand2tetris. We write a program in Jack and test it on VMEmulator. The class looks like this:
class List {
field int data;
field List next;
/* Creates a new List object. */
constructor List new(int car, List cdr) {
let data = car;
let next = cdr;
return this;
}
/* Disposes this List by recursively disposing its tail. */
method void dispose() {
if (~(next = null)) {
do next.dispose();
}
// Use an OS routine to recycle the memory held by this object.
do Memory.deAlloc(this);
return;
}
/* Prints the list*/
method void print() {
do Output.printString(" -> ");
do Output.printInt(data);
if (~(next = null)) {
do next.print();
}
return;
}
/* Inserts the argument in the right position of the list (ascending order)*/
method void insertInOrder(int ins){
var List prev, curr, insert;
let prev = this;
let curr = prev.getnext();
while (ins > prev.getdata()){
if (ins < curr.getdata()){
let insert = List.new(ins, curr);
do prev.setnext(insert);
}
else{
let prev = prev.getnext();
let curr = prev.getnext();
}
}
return;
}
/* Searches the argument in the list, if found, it returns the corresponding List object*/
method List find(int toFind){
var List temp;
var List equal;
var boolean found;
let temp = this;
let found = false;
while (~(next = null)){
if(toFind = temp.getdata()){
let equal = temp;
let found = true;
}
let temp = temp.getnext();
}
if (found){
return equal;
}
else{
return null;
}
}
method List getnext(){
return next;
}
method void setnext(List object){
let next = object;
return;
}
method int getdata(){
return data;
}
}
It has one private variable data and a pointer next. So I wrote getter and setter method to return those values. Other methods are fine only the getdata()method is incorrect. When it runs through the VMEmulator, it shows the error Out of segment space in List.getdata.3. This shows in the VMEmulator.
0function List.getdata0
1push argument0
2pop pointer0
3push this 0
4return
the error is at the 4th line return. When I change the Jack code, the same error is still at the 4th line.
What exactly is the problem in my getter method?
When you run a VM program on the VMEmulator you must first manually set the pointers to the various segments, otherwise you may get an "Out of segment space" error.
To understand the necessary settings, look at what the corresponding .tst file does. An alternative method is to insert the proposed code inside a function, since the function call automatically makes this type of setting.
You can get this error when you try to access member data of an object which is not constructed. Could it be that the List cdr in the constructor was not properly constructed?

Is there a way to merge filter and map into single operation in Kotlin?

The below code will look for "=" and then split them. If there's no "=", filter them away first
myPairStr.asSequence()
.filter { it.contains("=") }
.map { it.split("=") }
However seeing that we have both
.filter { it.contains("=") }
.map { it.split("=") }
Wonder if there's a single operation that could combine the operation instead of doing it separately?
You can use mapNotNull instead of map.
myPairStr.asSequence().mapNotNull { it.split("=").takeIf { it.size >= 2 } }
The takeIf function will return null if the size of the list returned by split method is 1 i.e. if = is not present in the string. And mapNotNull will take only non null values and put them in the list(which is finally returned).
In your case, this solution will work. In other scenarios, the implementation(to merge filter & map) may be different.
I see your point and under the hood split is also doing an indexOf-check to get the appropriate parts.
I do not know of any such function supporting both operations in a single one, even though such a function would basically just be similar to what we have already for the private fun split-implementation.
So if you really want both in one step (and require that functionality more often), you may want to implement your own splitOrNull-function, basically copying the current (private) split-implementation and adapting mainly 3 parts of it (the return type List<String>?, a condition if indexOf delivers a -1, we just return null; and some default values to make it easily usable (ignoreCase=false, limit=0); marked the changes with // added or // changed):
fun CharSequence.splitOrNull(delimiter: String, ignoreCase: Boolean = false, limit: Int = 0): List<String>? { // changed
require(limit >= 0, { "Limit must be non-negative, but was $limit." })
var currentOffset = 0
var nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
if (nextIndex == -1 || limit == 1) {
if (currentOffset == 0 && nextIndex == -1) // added
return null // added
return listOf(this.toString())
}
val isLimited = limit > 0
val result = ArrayList<String>(if (isLimited) limit.coerceAtMost(10) else 10)
do {
result.add(substring(currentOffset, nextIndex))
currentOffset = nextIndex + delimiter.length
// Do not search for next occurrence if we're reaching limit
if (isLimited && result.size == limit - 1) break
nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
} while (nextIndex != -1)
result.add(substring(currentOffset, length))
return result
}
Having such a function in place you can then summarize both, the contains/indexOf and the split, into one call:
myPairStr.asSequence()
.mapNotNull {
it.splitOrNull("=") // or: it.splitOrNull("=", limit = 2)
}
Otherwise your current approach is already good enough. A variation of it would just be to check the size of the split after splitting it (basically removing the need to write contains('=') and just checking the expected size, e.g.:
myPairStr.asSequence()
.map { it.split('=') }
.filter { it.size > 1 }
If you want to split a $key=$value-formats, where value actually could contain additional =, you may want to use the following instead:
myPairStr.asSequence()
.map { it.split('=', limit = 2) }
.filter { it.size > 1 }
// .associate { (key, value) -> key to value }

Why aren't the entries in a Kotlin Pair mutable?

I have a MutableList of Pairs and I'd like to decrement the value of the first entry so my condition my pass(change):
while(n > 0) {
if(sibice[i].first > 0) {
sum += sibice[i].second
//sibice[i].first-- will not compile
n--
} else i++
}
But the Pair class doesn't let me do that, besides creating my own pair is there any other workaround and why is this even the case?
Like with all entities, issues arise with mutability.
In your case you can just update the list-entry with a new pair of values.
val newPair = oldPair.copy(first = oldPair.first-1)
Or directly use an array of length 2 instead intArrayOf(0, 0). So you can access the elements directly.
while(n > 0) {
if(sibice[i][0] > 0) {
sum += sibice[i][1]
sibice[i][0]--
n--
} else i++
}
You could even define extension values first and second to the IntArray type and use it the same like before.
val IntArray.second get() = get(1)
var IntArray.first
set(value) = set(0, value)
get() = get(0)

Find instead of Filter Array in Swift

I have an array of PFObjects. I'd like to search if the "Type" contains "Sushi". The filter alters the array. How can I preform this search without altering the array?
func startCheckOptions(objects: [AnyObject]) {
let filteredArray = objects.filter() {
if let type = ($0 as PFObject)["Type"] as String {
//if "type" contains "sushi", then do something instead of alter array
return type.rangeOfString("Sushi") != nil
} else {
return false
}
}
}
You can use the contains function:
func startCheckOptions(objects: [AnyObject]) -> Bool {
return contains(objects as [PFObject]) { (object) -> Bool in
if let type = object["Type"] as? String {
return type.rangeOfString("Sushi") != nil
}
else {
return false
}
}
}
if startCheckOptions(objects) {
println("yes")
}
else {
println("no")
}
This has the advantage of not building a new array containing the matching objects and stopping on the first match.
In this case you'd be better off protecting the objects cast (or really handling one time somewhere else) by casting it to [PFObject] ASAP. Leaving AnyObject references floating around can only lead to confusion and heartache.
filter() does not alter an array, but it returns a new array. What you have above is correct. objects will be the original array, and filteredArray will be the new array consistent of objects where "type" is "sushi".