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
Related
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)
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]
I want convert string to Map in grails. I already have a function of string to map conversion. Heres the code,
static def StringToMap(String reportValues){
Map result=[:]
result=reportValues.replace('[','').replace(']','').replace(' ','').split(',').inject([:]){map,token ->
List tokenizeStr=token.split(':');
tokenizeStr.size()>1?tokenizeStr?.with {map[it[0]?.toString()?.trim()]=it[1]?.toString()?.trim()}:tokenizeStr?.with {map[it[0]?.toString()?.trim()]=''}
map
}
return result
}
But, I have String with comma in the values, so the above function doesn't work for me. Heres my String
[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]
my function returns ABC only. not ABC, INC. I googled about it but couldnt find any concrete help.
Generally speaking, if I have to convert a Stringified Map to a Map object I try to make use of Eval.me. Your example String though isn't quite right to do so, if you had the following it would "just work":
// Note I have added '' around the values.
String a = "[program_type:'', subsidiary_code:'', groupName:'', termination_date:'', effective_date:'', subsidiary_name:'ABC']"
Map b = Eval.me(a)
// returns b = [program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC]
If you have control of the String then if you can create it following this kind of pattern, it would be the easiest solution I suspect.
In case it is not possible to change the input parameter, this might be a not so clean and not so short option. It relies on the colon instead of comma values.
String reportValues = "[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
reportValues = reportValues[1..-2]
def m = reportValues.split(":")
def map = [:]
def length = m.size()
m.eachWithIndex { v, i ->
if(i != 0) {
List l = m[i].split(",")
if (i == length-1) {
map.put(m[i-1].split(",")[-1], l.join(","))
} else {
map.put(m[i-1].split(",")[-1], l[0..-2].join(","))
}
}
}
map.each {key, value -> println "key: " + key + " value: " + value}
BTW: Only use eval on trusted input, AFAIK it executes everything.
You could try messing around with this bit of code:
String tempString = "[program_type:11, 'aa':'bb', subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
List StringasList = tempString.tokenize('[],')
def finalMap=[:]
StringasList?.each { e->
def f = e?.split(':')
finalMap."${f[0]}"= f.size()>1 ? f[1] : null
}
println """-- tempString: ${tempString.getClass()} StringasList: ${StringasList.getClass()}
finalMap: ${finalMap.getClass()} \n Results\n finalMap ${finalMap}
"""
Above produces:
-- tempString: class java.lang.String StringasList: class java.util.ArrayList
finalMap: class java.util.LinkedHashMap
Results
finalMap [program_type:11, 'aa':'bb', subsidiary_code:null, groupName:null, termination_date:null, effective_date:null, subsidiary_name:ABC, INC:null]
It tokenizes the String then converts ArrayList by iterating through the list and passing each one again split against : into a map. It also has to check to ensure the size is greater than 1 otherwise it will break on f[1]
How do I specify a character literal in groovy since both 'a' and "a" result in string?
I do not want to declare a character variable just for this purpose.
Using the as keyword is the way to make a character literal in Groovy.
'a' as char
See the discussion here at Groovy's buglist.
If this is for a variable, you can also define the type, so:
import java.awt.image.*
new BufferedImage( 1, 1, BufferedImage.TYPE_INT_RGB ).with {
createGraphics().with {
// Declare the type
char aChar = 'a'
// Both ways are equivalent and work
assert fontMetrics.charWidth( aChar ) == fontMetrics.charWidth( 'a' as char )
dispose()
}
}
(apologies for the long example, but I had brain freeze, and couldn't think of a different standard java function that takes a char) ;-)
This also goes against the second line of the question, but I thought I'd add it for completeness
There a three ways to use char literals in Groovy:
char c = 'c' /* 1 */
'c' as char /* 2 */
(char) 'c' /* 3 */
println Character.getNumericValue(c) /* 1 */
println Character.getNumericValue('c' as char) /* 2 */
println Character.getNumericValue((char) 'c') /* 3 */
If you assign a String literal like 'c' to a variable, Groovy does the cast implicitly (see /* 1 * /). If you want use the String literals without variables, you have to cast them by using ...as char... (see /* 2 * /) or ...(char)... (see /* 3 * /).
The usage of char literals in methods without casting them is not possible as Groovy has only String literals which must be casted to char.
println Character.getNumericValue('c') // compile error
This response is rather late! But just stumbled upon it and wanted to add some clarification.
The more accurate Answer is unlike Java, Groovy does NOT have a character literal, but you can cast a string to a character. A literal is a value that is written exactly as it is to be interpreted, and the necessity of the type cast indicates it is NOT truly a literal.
Examples:
assert 'a'.class != Character.class
assert 'a'.class == String.class
assert ('a' as char).class == Character.class
assert ((char)'a').class == Character.class
char A = 'a'; // implicit coercion of string to char
assert A.class == Character.class
In contrast, both groovy and Java support numeric literals for int, long, double, and float, but do not support numeric literal for short.
Examples:
assert 42.class == Integer.class
assert 42l.class == Long.class
assert 42f.class == Float.class
assert 42d.class == Double.class
assert (42 as Short).class == Short.class
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
}