How can i call an interface in kotlin? - kotlin

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
}

Related

Should I get rid of big switch case?

I have a factory which includes many HTML attribute generators which returns one of them based on the type of attribute, so I wanted to see if there is a better way of doing this.
class AttributeHtmlGeneratorFactory {
fun create(property: String): AttributeHtmlGenerator {
when (property) {
"animation" -> {
return AnimationHtmlGenerator()
}
...
"left", "top" -> {
return PositionHtmlGenerator()
}
...
"scaleX" , "scaleY", ... , "direction" -> {
return UnusedAttributesHtmlGenerator()
}
this when switch has like 20 switch cases in it.
this is the interface which all these classes are using
interface AttributeHtmlGenerator {
fun generateHtml(member: KProperty1<HtmlComponentDataModel, *>, component: HtmlComponentDataModel ): String
}
and this is where and how I'm using all of these:
var result = ""
HtmlComponentDataModel::class.memberProperties.forEach { member ->
val generator = AttributeHtmlGeneratorFactory().create(member.name)
result = result.plus(generator.generateHtml(member, component))
}
return result
also, this is a simple implementation of the interface:
class ButtonFillHtmlGenerator : AttributeHtmlGenerator {
override fun generateHtml(member: KProperty1<HtmlComponentDataModel, *>, component: HtmlComponentDataModel): String {
var result = ""
member.get(component)?.let {
result = result.plus("background-color:${it};")
}
return result
}
}
is there anyway to make this better?
If you just want to reformat the when statement, I suggest you you do like this:
fun create(property: String): AttributeHtmlGenerator = when (property)
{
"animation" -> AnimationHtmlGenerator()
"left", "top" -> PositionHtmlGenerator()
"scaleX", "scaleY", "direction" -> UnusedAttributesHtmlGenerator()
else -> error("No generator found for property $property")
}
If you want to split this logic across modules, you would use a Map.
class AttributeHtmlGeneratorFactory {
private val generatorMap = mutableMapOf<String, () -> AttributeHtmlGenerator>()
init {
assignGeneratorToProperties("animation") { AnimationHtmlGenerator() }
assignGeneratorToProperties("left", "top") { PositionHtmlGenerator() }
}
fun create(property: String): AttributeHtmlGenerator {
return generatorMap[property]?.invoke() ?: error("No generator found for property $property")
}
fun assignGeneratorToProperties(vararg properties: String, provider: () -> AttributeHtmlGenerator) {
properties.forEach {
generatorMap[it] = provider
}
}
}
This way you can call assignGeneratorToProperties in parts of the code and thus split the initialization logic.
Performance-wise, when/if-else statements are really performant when you have a few cases but a HashMap outperforms them for a lot of elements. You decide what to use depending on your case.

How to express in Kotlin "assign value exactly once on the first call"?

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.

Kotlin: How to get a type of a method parameter

I know I can get the type of a method parameter by using "Method#parameters#name".
However, my parameters are all the subclass of A and I dont want to get the type A. I want to get the subclass name.
if (checkMethod(i)) {
val type = i.parameters[0].simpleName
if (!functions.containsKey(type)) {
functions[type] = HashMap()
}
if (!functions[type]?.containsKey(identifier)!!) {
functions[type]?.put(identifier, ArrayList())
}
functions[type]?.get(identifier)?.add(i)
}
Final Solution:
private fun analysis(clazz: KClass<EventHandler>, identifier: String) {
clazz.members.forEach {
if(it is KFunction) {
if(checkMethod(it)) {
val type = methodEventType(it)
if(!invokeMethods.containsKey(type)) invokeMethods[type] = HashMap()
if(!invokeMethods[type]!!.containsKey(identifier)) invokeMethods[type]!![identifier] = ArrayList()
invokeMethods[type]!![identifier]!!.add(it.javaMethod)
}
}
}
}
private fun checkMethod(method: KFunction<*>): Boolean {
method.annotations.forEach {
if(it is EventSubscriber) {
val type = method.parameters[1].type.classifier
if(type is KClass<*>) {
if(method.parameters.size == 2 && type.superclasses.contains(Event::class)) {
return true
}
}
}
}
return false
}
And notice here. I dont know why the method`s first parameter is allways a instance of its class. So the real parameter is start from 1 instead of 0.
Maybe you'll find this example useful (works with kotlin-reflect:1.4.21)
import kotlin.reflect.full.createType
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.jvm.reflect
open class A
val aType = A::class.createType()
class B: A()
class C: A()
val foo = { b: B, c: C ->
println(b)
println(c)
}
println(foo.reflect()!!.parameters[0].type.classifier == B::class) // true
println(foo.reflect()!!.parameters[1].type.classifier == C::class) // true
println(foo.reflect()!!.parameters[0].type.isSubtypeOf(aType)) // true
To get all subclasses
println((foo.reflect()!!.parameters[0].type.classifier as KClass<*>).allSuperclasses.contains(A::class)) // true
Try this to get the class of the first parameter:
i.parameters[0]::class.java

Parking lot project error: when using a scanner. NoSuchElementException

I'll appreciate all your help.
I've been working on a course project where I have to make a parking lot that registers cars. When I use it in my IDE it works fine but when I run it through the platforms tests, in the first one, there's no problem but when the second iteration reaches the "when (val command = scanner.next())" in the createOrder fun, it crashes with the error:
java.lang.AssertionError: Exception in test #1
Probably your program run out of input (Scanner tried to read more than expected).
java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:862)
at java.util.Scanner.next(Scanner.java:1371)
at parking.ParkingLot.createOrder(Main.kt:39)
at parking.ParkingLot.start(Main.kt:31)
at parking.MainKt.main(Main.kt:6)
at parking.MainKt.main(Main.kt)
Please find below the output of your program during this failed test.
Note that the '>' character indicates the beginning of the input line.
---
> park KA-01-HH-1234 White
White car parked in spot 1.
the idea is that the test inputs many cars but it crashes when trying to do the second input
this is my code (sorry if my code is messy, I'm still learning)
import java.util.*
fun main() {
ParkingLot.start()
}
class Car(val regNumber: String = "", val color: String = "") {
}
class Order(val command: String) {
lateinit var regNum: String
lateinit var color: String
lateinit var spot: String
lateinit var status: String
}
object ParkingLot {
val spaces: Array<Pair<String?, Car?>> = Array(20) { Pair(null, null) }
const val occupied = "occupied"
const val park = "park"
const val leave = "leave"
const val exit = "exit"
fun start() {
val scanner = Scanner(System.`in`)
do {
val order = createOrder(scanner)
interaction(order, scanner)
} while (order.command != exit)
}
fun createOrder(scanner: Scanner): Order {
when (val command = scanner.next()) {
park -> {
val parkOrder = Order(command)
parkOrder.regNum = scanner.next()
parkOrder.color = scanner.next()
parkOrder.status = "valid"
return parkOrder
}
leave -> {
val retrieveOrder = Order(command)
retrieveOrder.spot = scanner.next()
retrieveOrder.status = "valid"
return retrieveOrder
}
exit -> {
val exitOrder = Order(command)
exitOrder.status = "valid"
return exitOrder
}
else -> {
val incorrectOrder = Order(command)
incorrectOrder.status = "invalid"
return incorrectOrder
}
}
}
fun interaction(order: Order, scanner: Scanner) {
if (order.command == park) {
// val toParkCar = Car(order.regNum, order.color)
park(Car(order.regNum, order.color))
}
if (order.command == leave) {
leave(order)
}
if (order.command == exit) return
//TODO update the error msg to include exit command
if (order.status == "invalid") println("\"${order.command}\" isn't a valid , either use \"park\" or \"leave\"")
// scanner.close()
}
fun park(car: Car) {
for ((index, item) in spaces.withIndex()) {
if (item.first == null) {
spaces[index] = Pair(occupied, car)
println("${car.color} car parked in spot ${index + 1}.")
return
}
}
println("Sorry, the parking lot is full.")
}
fun leave(order: Order) {
if (spaces[order.spot.toInt() - 1].first == occupied) {
spaces[order.spot.toInt() - 1] = Pair(null, null)
println("Spot ${order.spot} is free.")
} else {
println("There is no car in spot ${order.spot}.")
}
}
}
Ok so I noticed this is a problem for the JetBrains plugin. I don't know why but the solution was taking the scanner out of the function and directly in the main loop.

Kotlin general setter function

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.