In this code what is the difference between using a for or each function ?
Which is better?
for (dev in settings.speaker) {
dev.initialize()
if(logEnable == true)log.info "${app.label} doing the busness on $dev"
pause(3000)
}
/*
settings.speaker.each { it ->
it.initialize()
if(logEnable == true) log.trace "${app.label} initializing $it"
pause(3000)
}
The second is less typing.
(Especally if you forget the it ->
at the beginning and just let that part be done for you automatically.)
The first comes from Java, and most Java is valid Groovy. The each()
method is a Groovy addition that, coupled with a closure, lets you do something similar with less typing and in what Groovy proponents would call a more Groovy-esque manner. (I also happen to think it's easier to type and clearer to read.) But that's more or less why both exist.
What differences there may be by the time either gets compiled to Java bytecode, I can't say...
2 Likes
Thanks for that, I was think which is more efficient for the hub if you were doing a lot of repeats
I suppose we can't know for sure without benchmarking on the JVM that Hubitat apps/drivers run in, which there isn't an easy way to do. If I had to guess, I'd say the Java way probably incurs slightly greater efficiency (though probably less so than if things were statically compiled). However, from a practical perspective -- the collections you'd be iterating over in an app and driver are likely to be rather small, apps and drivers generally only run for a few dozen or maybe a few hundred milliseconds at a time, and the hub sits idle most of the time -- I can't imagine it would really make a difference. I go with the one that is easier for me to write and maintain, so my preferences is each()
.
(Oh, and see also: that "premature optimization is the root of all evil" quote.)
Some users have reported that avoiding def
/Object
typing where possible is faster/more efficient on Hubitat (it probably is in general, but this specific advice comes from the current maintainer of webCoRE, perhaps the largest community app--or app at all--on Hubitat, so I'm sure he has numbers to back this up). Thus, if you know you're dealing with devices, probably the best thing you can do with either is specify the object type, something like:
settings.speaker.each { com.hubitat.app.DeviceWrapper dev ->
dev.initialize()
if(logEnable == true) log.trace "${app.label} initializing $dev"
pause(3000)
}
But even then I probably wouldn't worry too much about this for most apps.
2 Likes
What does the device wrapper do, I try to use int/String/float but don't really understand that wrapper bit
That is the way you specify the type of the object (you could have kept the name it
if you wanted, but without a type specified, it's just an Object
--not a String
, Integer,
etc. as it sounds like you are already familiar with; specifically typing objects is what I'm saying others have identified as helpful in larger apps/drivers).
What might not be apparent is that com.hubitat.app.DeviceWrapper
is the "full" name of the DeviceWrapper
class (which you have to use unless you do something like import com.hubitat.app.DeviceWrapper
elsewhere, which I normally do to make this easier to type). It may also not be apparent that DeviceWrapper
itself exists at all, but it's what you get with app input
s that select devices (vaguely alluded to in the docs; also note that with multiple: true
, you get a DeviceWrapperList
that contains individual DeviceWrapper
objects).
Closures (2nd case) is interesting in the JVM. This is a common 'issue area', so if you have choices of minimizing closures, it may help....
So perfect would be
Com.hubitat.appDeviceWrapperList settings.speaker.each { com.hubitat.app.DeviceWrapper dev ->
dev.initia.......
I've read a lot about closures but still don't really understand what they are and how to avoid?
No, you do not need the type before settings.speaker
(that object is already defined).
So whenever you want to call a dev from a setting you should use this
Eg
com.hubitat.app.DeviceWrapper speaker.initialize()
This would trigger initialize on all devices selected, at the same time?
No, if the object you're referencing is already defined (as anything that comes from an app input
is, and presumably that is what speaker
is in your context), you do not need to specify the type. That is when you are creating/defining the variable.
You can also just totally ignore the advice about typing as opposed to def
and whatnot, too--for small apps/drivers, it's unlikely to make much of a difference, just something nh.schottfam noticed. Most apps aren't webCoREian in size.
And it's what you'll find in most Groovy examples.
2 Likes
I get it, it because dev -> is a new variable so defining its object type
Can you elaborate/ explain , are you saying the 1st or 2nd example is better?
closures can be starting points for memory leaks in older jvms , so for code that runs a lot, need to be careful with them and keep them 'simple' as much as possible.
so which was better to reduce chance of memory leak?
avoid closures or keep them simple
so like the the live code is safer than the commented out code?
private zwaveEvent(hubitat.zwave.commands.thermostatmodev2.ThermostatModeReport cmd ) {
String mode = ''
switch (cmd.mode) {
case 0x01:
mode = 'heat'
break;
case 0x00:
mode = 'off'
break;
case 0x0F:
mode = 'emergency heat'
break;
case 0x0B:
mode = 'cool'
break;
default:
log.warn 'no mode'
}
/*
if (cmd.mode == 0x01){ //1 normall heat 0x01 1
mode = 'heat'
}
else if (cmd.mode == 0x0F){ //15 boost 0x0F 15 == true
mode = 'emergency heat'
}
else if (cmd.mode == 0x0B){ //11 eco 11 0x0B 11
mode = 'cool'
}
else if (cmd.mode == 0x00){ // 0 off 0x00 0
mode = 'off'
sendEvent(name: 'thermostatOperatingState', value: 'idle')
}
else{log.warn 'no mode'}
*/
sendEvent(name: 'thermostatMode', value: mode)
}
those don't appear to be closures
so either or