Kotlin getString() exepects an Int? Why? - android-studio

I'm going through https://developer.android.com/codelabs/basic-android-kotlin-training-project-lemonade. Here, while trying to make "else" do nothing, I've stumbled upon "Type mismatch" errors.
Regarding setting the view elements, this is what I've come up with so far:
private fun setViewElements() {
val textAction: TextView = findViewById(R.id.text_action)
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
RESTART -> R.string.lemon_empty_glass
else -> null
}
textAction.text = getString(text)
I've tried all workarounds for "doing nothing": {}, Unit, null, empty string (and even data types like String, Int, Double...) but all I get is a "Type mismatch: inferred type is Any but Int was expected" (or "...inferred type is Int?...") error. So much so that an Int does makes the error disapear, as in:
...
else -> 0
According to the docs, both versions of getString(), single and double paramater, work stritcly with strings: parameter(s) and return type. So why on Earth is it saying this function expects an Int?
Also, writing text as an instance of Int (text: Int), doesn't affect anything, meaning it is in fact an Int. I am missing something big here: aren't those R.string.<name> supposed to be strings?
Btw, I did try this:
private fun setViewElements() {
val textAction: TextView = findViewById(R.id.text_action)
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
else -> R.string.lemon_empty_glass
}
textAction.text = getString(text)
which is errorless and looks better. However, I wanted to keep the specificity, if possible (and it doesn't answer my question).

As the comments pointed out, getString takes a Int, which is a resource ID. This identifies one of the strings you have written in your XML files. This way, you can easily have e.g. multiple versions of the string, one for each localisation for your app, and getString will figure out which string to use based on the device locale. R.string.lemon_select, R.string.lemon_squeeze etc are those identifiers.
One way to do nothing, is to return just evaluate to null - you just have to handle the null value yourself. This will cause text to be of type Int?.
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
RESTART -> R.string.lemon_empty_glass
else -> null
}
if (text != null) {
textAction.text = getString(text)
}
From your last code snippet though, it seems like you want to set it to lemon_empty_glass when the else branch is reached. If that is the case, then you can do:
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
RESTART -> R.string.lemon_empty_glass
else -> null
}
textAction.text = getString(text ?: R.string.lemon_empty_glass)

Related

How can I make Spinner function stop crashing my app when trying to get the selected item?

I'm having Trouble getting string values from a spinner without it crashing my app , I want to make a choice according to the 2 pair of selected items in the when Function
val convertFrom = spnConvertFrom.selectedItem.toString()
val convertTo = spnConvertTo.selectedItem.toString()
val value = initialAmount.toString()
var valor2= value.toDouble()
when {
//Condicion
(convertFrom.equals("NIO") && convertTo.equals("USD")) -> currencyConverted.apply {
text = "Something"
}
else -> Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show()
}
I've tried to switch the syntax a bit from = to .equals() (Same thing) Read something about it being null at the moment of the comparison but have no idea how to check it , I'm quite new to Kotlin and programing in android
There are several lines which could case an NPE
The lines where you are retrieving the selectedItem. You can handle those by adding the ? operator after the spinner instances
The line where you convert value to a Double. You can avoid the error by using toDoubleOrNulland providing a default value in case thatvalue` is not numeric
The line where you set the text of currencyConverted. If it is optional you should add the ? operator there as well:
val convertFrom = spnConvertFrom?.selectedItem.toString()
val convertTo = spnConvertTo?.selectedItem.toString()
val value = initialAmount.toString()
var valor2 = value.toDoubleOrNull() ?: 0.0
when {
//Condicion
(convertFrom.equals("NIO") && convertTo.equals("USD")) -> currencyConverted?.text = "Something"
else -> Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show()
}

Split string every n characters

What would be an idiomatic way to split a string into strings of 2 characters each?
Examples:
"" -> [""]
"ab" -> ["ab"]
"abcd" -> ["ab", "cd"]
We can assume that the string has a length which is a multiple of 2.
I could use a regex like in this Java answer but I was hoping to find a better way (i.e. using one of kotlin's additional methods).
Once Kotlin 1.2 is released, you can use the chunked function that is added to kotlin-stdlib by the KEEP-11 proposal. Example:
val chunked = myString.chunked(2)
You can already try this with Kotlin 1.2 M2 pre-release.
Until then, you can implement the same with this code:
fun String.chunked(size: Int): List<String> {
val nChunks = length / size
return (0 until nChunks).map { substring(it * size, (it + 1) * size) }
}
println("abcdef".chunked(2)) // [ab, cd, ef]
This implementation drops the remainder that is less than size elements. You can modify it do add the remainder to the result as well.
A functional version of chunked using generateSequence:
fun String.split(n: Int) = Pair(this.drop(n), this.take(n))
fun String.chunked(n: Int): Sequence<String> =
generateSequence(this.split(n), {
when {
it.first.isEmpty() -> null
else -> it.first.split(n)
}
})
.map(Pair<*, String>::second)
Output:
"".chunked(2) => []
"ab".chunked(2) => [ab]
"abcd".chunked(2) => [ab, cd]
"abc".chunked(2) => [ab, c]

Groovy Cast primitive type

I want to set fields dynamically in groovy so I have a java code that get datas from a database and I set groovy fields using Bindings.setVariable() in java.
I would like to know if it is possible to cast each primitive types in groovy.
String to int, String to float and so on.
So I could always send a string and cast in an other primitive times, it depends of the type of my groovy fields.
#Opal's as solution is good, but I also wanted to mention that the Groovy JDK adds some convenient primitive check and conversion methods to CharSequence, which String implements:
isDouble and asDobule
isFloat and asFloat
isLong and asLong
isInteger and asInteger
Interestingly, it isFloat seems to be greedy, returning true for floating points beyond its range.
['2', '2.2', '2' * 10, "${Double.MAX_VALUE}"].each { s ->
switch (s) {
case { it.isInteger() }:
int i = s.toInteger()
println "String '$s' -> int $i"
break
case { it.isLong() }:
long l = s.toLong()
println "String '$s' -> long $l"
break
case { it.isFloat() }:
float f = s.toFloat()
println "String '$s' -> float $f"
break
case { it.isDouble() }:
double d = s.toDouble()
println "String '$s' -> double $d"
break
default:
println "$s is not a supported primitive"
}
}
prints out
String '2' -> int 2
String '2.2' -> float 2.2
String '2222222222' -> long 2222222222
String '1.7976931348623157E308' -> float Infinity
It depends on what you exactly need, but the following piece of code works well:
assert '2' as Integer == 2
assert '2.2' as Double == 2.2

Is it possible to use F# record's labels as functions like in Haskell, or something similar?

In Haskell, given this record:
data ARecord { labelA :: String, labelB :: Int }
we get this functions:
labelA :: ARecord -> String
labelB :: ARecord -> Int
F# doesn't seem to work this way. But, is there something similar?
Edit
By something similar I mean something that saves me from having to define the functions manually, as #kaefer suggested.
It's easily defined.
type ARecord = { labelA : string; labelB : int }
let labelA { labelA = s } = s
// val labelA : ARecord -> string
Edit
The following function would compile to identical IL, with direct read access to the backing field, instead of the automatically generated instance property.
In contrast to normal experience with object-orientated dot-notation, it doesn't require type annotation to determine the record type.
let labelA' aRecord = aRecord.labelA
// val labelA' : aRecord:ARecord -> string

How do I use groovy's AS keyword

This may be a duplicate but "as" is an INCREDABLY hard keyword to google, even S.O. ignores "as" as part of query.
So I'm wondering how to implement a class that supports "as" reflexively. For an example class:
class X {
private val
public X(def v) {
val=v
}
public asType(Class c) {
if (c == Integer.class)
return val as Integer
if(c == String.class)
return val as String
}
}
This allows something like:
new X(3) as String
to work, but doesn't help with:
3 as X
I probably have to attach/modify the "asType" on String and Integer somehow, but I feel any changes like this should be confined to the "X" class... Can the X class either implement a method like:
X fromObject(object)
or somehow modify the String/Integer class from within X. This seems tough since it won't execute any code in X until X is actually used... what if my first usage of X is "3 as X", will X get a chance to override Integer's asType before Groovy tries to call is?
As you say, it's not going to be easy to change the asType method for Integer to accept X as a new type of transformation (especially without destroying the existing functionality).
The best I can think of is to do:
Integer.metaClass.toX = { -> new X( delegate ) }
And then you can call:
3.toX()
I can't think how 3 as X could be done -- as you say, the other way; new X('3') as Integer is relatively easy.
Actually, you can do this:
// Get a handle on the old `asType` method for Integer
def oldAsType = Integer.metaClass.getMetaMethod( "asType", [Class] as Class[] )
// Then write our own
Integer.metaClass.asType = { Class c ->
if( c == X ) {
new X( delegate )
}
else {
// if it's not an X, call the original
oldAsType.invoke( delegate, c )
}
}
3 as X
This keeps the functionality out of the Integer type, and minimizes scope of the effect (which is good or bad depending on what you're looking for).
This category will apply asType from the Integer side.
class IntegerCategory {
static Object asType(Integer inty, Class c) {
if(c == X) return new X(inty)
else return inty.asType(c)
}
}
use (IntegerCategory) {
(3 as X) instanceof X
}

Resources