The following code can work well, but the code of fun addDetail(...) is too complex, is there a simple way to do that ? Thanks!
BTW, in the fun addDetail(...), aMListDetail maybe null, and aMListDetail?.innerListDetail maybe null.
data class MDetail (
val _id: Long
)
class DetailsHandler(mContext: Context = UIApp.instance) {
data class MListDetail(val innerListDetail: MutableList<MDetail>)
private var aMListDetail: MListDetail?
var mJson: String by PreferenceTool(mContext,"mySavedJson", "")
init {
aMListDetail= Gson().fromJson(mJson,MListDetail::class.java)
}
fun addDetail(aMDetail:MDetail){
if (aMListDetail==null){
aMListDetail=MListDetail(mutableListOf(aMDetail))
}else{
if (aMListDetail?.innerListDetail==null){
aMListDetail=MListDetail(mutableListOf(aMDetail))
}else {
aMListDetail?.innerListDetail?.add(aMDetail)
}
}
mJson = Gson().toJson(aMListDetail)
}
}
fun addDetail(aMDetail: MDetail) {
if (aMListDetail?.innerListDetail == null) {
aMListDetail = MListDetail(mutableListOf(aMDetail))
} else {
aMListDetail.innerListDetail.add(aMDetail)
}
mJson = Gson().toJson(aMListDetail)
}
Alternative:
fun addDetail(aMDetail: MDetail) {
if (aMListDetail?.innerListDetail == null) {
aMListDetail = MListDetail(mutableListOf())
}
aMListDetail.innerListDetail.add(aMDetail)
mJson = Gson().toJson(aMListDetail)
}
You don't need null-safe ?. operators in your add() call, since at that point you've already checked that aMListDetail != null and innerListDetail != null.
BTW, in the fun addDetail(...), aMListDetail maybe null,
Why not fix the problem at the source? You initialize it in the constructor, then tell Kotlin it could be set to null, but actually you never do this!
If you remove the unused nullability, the code simplifies to:
class DetailsHandler(mContext: Context = UIApp.instance) {
data class MListDetail(val innerListDetail: MutableList<MDetail>)
var mJson: String by PreferenceTool(mContext,"mySavedJson", "")
// can even be val
private var aMListDetail: MListDetail
init {
aMListDetail= Gson().fromJson(mJson,MListDetail::class.java)
}
fun addDetail(aMDetail:MDetail){
aMListDetail.innerListDetail.add(aMDetail)
mJson = Gson().toJson(aMListDetail)
}
}
If your real code doesn't initialize it at the beginning, consider by lazy or by notNull.
and aMListDetail?.innerListDetail maybe null.
Only is aMListDetail is null, which you should avoid as above.
Finally, if you really need aMListDetail to be null sometimes, you can write
aMListDetail?.let {
it.innerListDetail.add(aMDetail)
}
(which does nothing if aMListDetail is null)
fun addDetail(aMDetail:MDetail){
if (aMListDetail?.innerListDetail==null){
aMListDetail=MListDetail(mutableListOf(aMDetail))
}else {
aMListDetail?.innerListDetail?.add(aMDetail)
}
mJson = Gson().toJson(aMListDetail)
}
8-)
Related
Looking for a natural Kotlin way to let startTime be initialized only in a particular place and exactly once.
The following naive implementation have two problems:
it is not thread safe
it does not express the fact "the variable was or will be assigned exactly once in the lifetime of an Item instance"
class Item {
var startTime: Instant?
fun start(){
if (startTime == null){
startTime = Instant.now()
}
// do stuff
}
}
I believe some kind of a delegate could be applicable here. In other words this code needs something similar to a lazy variable, but without initialization on first read, instead it happens only after explicit call of "touching" method. Maybe the Wrap calls could give an idea of possible implementation.
class Wrap<T>(
supp: () -> T
){
private var value: T? = null
private val lock = ReentrantLock()
fun get(){
return value
}
fun touch(){
lock.lock()
try{
if (value == null){
value = supp()
} else {
throw IllegalStateExecption("Duplicate init")
}
} finally{
lock.unlock()
}
}
}
How about combining AtomicReference.compareAndSet with a custom backing field?
You can use a private setter and make sure that the only place the class sets the value is from the start() method.
class Item(val value: Int) {
private val _startTime = AtomicReference(Instant.EPOCH)
var startTime: Instant?
get() = _startTime.get().takeIf { it != Instant.EPOCH }
private set(value) = check(_startTime.compareAndSet(Instant.EPOCH, value)) { "Duplicate set" }
fun start() {
startTime = Instant.now()
}
override fun toString() = "$value: $startTime"
}
fun main() = runBlocking {
val item1 = Item(1)
val item2 = Item(2)
println(Instant.now())
launch { println(item1); item1.start(); println(item1) }
launch { println(item1) }
delay(1000)
println(item2)
item2.start()
println(item2)
println(item2)
item2.start()
}
Example output:
2021-07-14T08:20:27.546821Z
1: null
1: 2021-07-14T08:20:27.607365Z
1: 2021-07-14T08:20:27.607365Z
2: null
2: 2021-07-14T08:20:28.584114Z
2: 2021-07-14T08:20:28.584114Z
Exception in thread "main" java.lang.IllegalStateException: Duplicate set
I think your Wrap class is a good starting point to implement this. I would definitely make it a property delegate and touch() could be much simplified:
fun touch() {
synchronized(this) {
check(value == null) { "Duplicate init" }
value = supp()
}
}
Then you can remove lock. But generally, this is a good approach.
If you would like to reuse lazy util from stdlib then you can do this by wrapping it with another object which does not read its value until asked:
class ManualLazy<T : Any>(private val lazy: Lazy<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? {
return if (lazy.isInitialized()) lazy.value else null
}
fun touch() {
lazy.value
}
}
class Item {
private val _startTime = ManualLazy(lazy { Instant.now() })
val startTime: Instant? by _startTime
fun start(){
_startTime.touch()
}
}
Of course, depending on your needs you can implement it in a much different way, using a similar technique.
This may be considered exploiting or hacking lazy util. I agree and I think Wrap approach is a better one.
I'm working on readlines now and can I make this few if's shorter? I'm making a validation to what user is sending to me. The filed cant be empty or null. I have 3 important things that user has to write in field and every three times I have to check the same... .
fun readlinesToAddEntryAndValidation(): List<String> {
println(ENTER_DESCRIPTION_ID_TEKST)
val entryId: String? = readLine()
if (!entryId.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_NAME_TEKST)
val name: String? = readLine()
if (!name.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_TEKST_TEKST)
val tekst: String? = readLine()
if (!tekst.isNullOrEmpty()) {
return listOf(entryId, name, tekst)
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
}
Try to avoid cognitive complexity one of the things is avoid nesting. Also when an if always returns something. An else statement is not needed
fun readlinesToAddEntryAndValidation(): List<String> {
println(ENTER_DESCRIPTION_ID_TEKST)
val entryId: String? = readLine()
if (entryId.isNullOrEmpty()) {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
println(ENTER_DESCRIPTION_NAME_TEKST)
val name: String? = readLine()
if (!name.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_TEKST_TEKST)
val tekst: String? = readLine()
if (!tekst.isNullOrEmpty()) {
return listOf(entryId, name, tekst)
}
}
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
You could do something like this:
fun readlinesToAddEntryAndValidation() : List<String> {
fun read(message: String): String? {
println(message)
val line = readLine()
return if (line.isNullOrEmpty()) null else line
}
read(ENTER_DESCRIPTION_ID_TEKST)?.let { entryId ->
read(ENTER_DESCRIPTION_NAME_TEKST)?.let { name ->
read(ENTER_DESCRIPTION_TEKST_TEKST)?.let { tekst ->
return listOf(entryId, name, tekst)
}
}
}
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
I wouldn't normally recommend nesting too much, but I feel like that's fairly readable with only three parameters, and the null checking means it short-circuits as soon as you run into a problem.
Making user to reenter all previous (independent!) values after his mistake in the middle of the input is a bad UI.
If user failed to correctly input some entry, you need to ask him to reenter only this single item (until he eventually do it right):
fun read(inputMessage: String, errorMessage: String = EMPTY_READLINE_ERROR): String {
println(inputMessage)
var line: String? = readLine()
while (line.isNullOrEmpty()) {
println(errorMessage)
println(inputMessage)
line = readLine()
}
return line
}
With this auxilary function, whole program become a single-liner:
fun readlinesToAddEntryAndValidation() =
listOf(
ENTER_DESCRIPTION_ID_TEKST,
ENTER_DESCRIPTION_NAME_TEKST,
ENTER_DESCRIPTION_TEKST_TEKST
).map { read(it) }
I do not have a project in my work and they have asked me to give me a pass, but after passing the whole project, there is a part that has given me a code error at the moment. Clearly it's my first time in Kotlin and I have no idea, but I do have an idea. I tried to solve it and I have not succeeded. So I was asking for help. I get an error right at the beginning of the
= SpeechService.Lintener {
Here the code
private val mSpeechServiceListener = SpeechService.Listener { text: String?, isFinal: Boolean ->
if (isFinal) {
mVoiceRecorder!!.dismiss()
}
if (mText != null && !TextUtils.isEmpty(text)) {
runOnUiThread {
if (isFinal) {
if (mText!!.text.toString().equals("hola", ignoreCase = true) || b == true) {
if (b == true) {
mText!!.text = null
mTextMod!!.text = text
repro().onPostExecute(text)
random = 2
} else {
b = true
mText!!.text = null
val saludo = "Bienvenido, ¿que desea?"
mTextMod!!.text = saludo
repro().onPostExecute(saludo)
}
}
} else {
mText!!.text = text
}
}
}
}
and here the interface
interface Listener {
fun onSpeechRecognized(text: String?, isFinal: Boolean)
}
Please, help me. the error is "Interface Listener does not have constructor"
The SpeechService.Listener { } syntax for SAM interfaces is only possible when the interface is written i Java (see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions). Because the interface is written in Kotlin, you have to write it like this:
private val mSpeechServiceListener = object : SpeechService.Listener {
override fun onSpeechRecognized(text: String?, isFinal: Boolean) {
// Code here
}
}
You don't really need the SpeechService.Listener interface in Kotlin though. You could just use a lambda function. This depends on whether the interface comes from a library or if you've written it yourself though.
private val mSpeechServiceListener: (String?, Boolean) -> Unit = { text, isFinal ->
// Code here
}
Unfortunately, I can't understand how to check Observable.
Depending on connection - I want to get my data from network or DB.
I have a method that checks network connection:
companion object {
fun isConnected() : Observable<Boolean> {
val connectivityManager = MyApplication.applicationContext().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetworkInfo
val isConnectedException = activeNetwork != null && activeNetwork.isConnectedOrConnecting
return Observable.just(isConnectedException)
}
}
So if it's true I want to call my network method:
override fun searchGroups(q: String): Observable<List<Group>> {
return groupApi.searchGroups(GroupSearchRequest(q).toMap())
.flatMap { groupResponse -> Observable.just(groupResponse.response.items) }
.doOnNext{ groupList -> groupRepository.insertGroups(groupList)}
}
and in the other case I want to call DB method:
override fun getGroupsFromDB(q: String): Observable<List<Group>> {
return groupRepository.findByName(q)
}
Here is my try to do this, but I think there is problem because of nullable interactor, but still don't know what to do.
compositeDisposable.add(
NetworkManager.isConnected()
.flatMap {
if (it) {
interactor?.searchGroups(q)
} else {
interactor?.getGroupsFromDB(q)
}
}
}
)
Could anybody please help me with that ?
UPDATE
So the problem was in nullable object interactor.
Could anybody please suggest the better way to not using !! for interactor object?
I am new to kotlin. I wonder if this is possible
I wish to create a function that will change the value of the properties of the object and return the object itself. The main benefit is that I can chain this setter.
class Person {
var name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
try {
// the line below caused error
this[propName] = value
} catch(e:Exception){
println(e.printStackTrace())
}
}
}
}
//usage
var person = Person(null,null)
person
.setter(name, "Baby")
.setter(age, 20)
But I get error "unknown references"
This question is marked as duplicate, however the possible duplicate question specifically want to change the property of "name", but I wish to change anyProperty that is pass from the function to object. Can't seem to connect the dot between two questions. #Moira Kindly provide answer that explain it. thankyou
Why not just simplify your answer to
fun setter(propName: String, value: Any): Person {
val property = this::class.memberProperties.find { it.name == propName }
when (property) {
is KMutableProperty<*> ->
property.setter.call(this, value)
null ->
// no such property
else ->
// immutable property
}
}
Java reflection isn't needed, its only effect is to stop non-trivial properties from being supported.
Also, if you call it operator fun set instead of fun setter, the
this[propName] = value
syntax can be used to call it.
After googling around, I think I can provide an answer, but relying on java instead of kotlin purely. It will be great if someone can provide a better answer in kotlin.
class Person(
var name: String,
val age: Int
){
fun setter(propName: String, value: Any): Person{
var isFieldExistAndNotFinal = false
try{
val field = this.javaClass.getDeclaredField(propName)
val isFieldFinal = (field.getModifiers() and java.lang.reflect.Modifier.FINAL == java.lang.reflect.Modifier.FINAL)
if(!isFieldFinal) {
// not final
isFieldExistAndNotFinal = true
}
// final variable cannot be changed
else throw ( Exception("field '$propName' is constant, in ${this.toString()}"))
} catch (e: Exception) {
// object does not have property
println("$e in ${this.toString()}")
}
if(isFieldExistAndNotFinal){
val property = this::class.memberProperties.find { it.name == propName }
if (property is KMutableProperty<*>) {
property.setter.call(this, value)
}
}
return this;
}
}
usage like this
person
.setter(propName = "age", value = 30.00)
.setter(propName = "asdf", value = "asdf")
.setter(propName = "name", value = "A Vidy")
You have error because when you do this[propName] = value you are trying to use this as a list, but it is not a list, it is a Person and it doesn't overload the [] operator.
What you can do is to add a check for the property that is setted:
class Person {
privavar name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
if (propName == "name" && value is String?) {
it.name = value as String?
} else if (propName == "age" && value is Int?) {
it.age = value as Int?
} else {
// handle unknown property or value has incorrect type
}
}
}
}
Another more dynamic solution without reflection:
class Person {
private var fields: Map<String, Any?> = HashMap()
fun setter(propName:String, value:Any): Person{
return this.apply {
it.fields[propName] = value;
}
}
fun getName() = fields["name"]
}
If you want to get rid of the getters as well then you need to use reflection.