Check if class is a valid value for KParameter - kotlin

Given the following example code
fun function(text: CharSequence) {
println(text)
}
val textParam = ::function.parameters[0]
val stringClass = String::class
How can I check if textParam accepts stringClass as a parameter?

You can do the following with KClass:
val paramClass = ::function.parameters[0].type.jvmErasure
println(stringClass.isSubclassOf(paramClass))
Alternatively, another solution with checking KType:
val paramType = ::function.parameters[0].type
println(stringClass.starProjectedType == paramType || // type is String
stringClass.allSupertypes.contains(paramType)) // type is a supertype of String

Related

Kotlin reflection on Anonymous object using kotlin-reflect

I am receiving a json object in which there is a property I don't know the name of at compile time.
The name of the property is stored in a variable.
Since the name of the property may vary the JSON is parsed as an Anonymous object.
Is it possible to read the value of the property using reflection using the name stored in the variable ?
I tried with code resembling this:
jsonResponse::class.memberProperties.find { it.name == variableName }
with no success.
val decodedToken = JWT(jwtString)
decodedToken.getClaim("useful_claim").asObject(Any::class.java)?.let {
// Get the property that matches the variable name
val reflectProp = res::class.memberProperties.find { it.name == BuildConfig.VARIABLE_NAME }
// Check that the property was found and exists
if (reflectProp is KMutableProperty<*>) {
(reflectProp.getter.call(res, BuildConfig.VARIABLE_NAME) as? List<*>)?.let {
// Return it as a list of existing MyClass
return it.filterIsInstance<MyClass>()
}
}
}
After the comments made by #Joffrey and #broot I tried without the JWT library.
Here is the code:
// Parcelable classes
#Parcelize
#JsonClass(generateAdapter = true)
data class JwtCustomResponse(
// This maps the variable name from the buildconfig on a known field name that can be used later
#field:Json(name = BuildConfig.VARIABLE_NAME) val appResourceAccess: MyCustomClass? = MyCustomClass()
) : Parcelable
#Parcelize
#JsonClass(generateAdapter = true)
data class JWTBody(
#field:Json(name = "resource_access") val resourceAccess: JwtCustomResponse? = JwtCustomResponse(),
) : Parcelable
// Custom JWT deserializer
object JWTUtils {
#Throws(Exception::class)
fun decoded(JWTEncoded: String): JWTBody {
val split = JWTEncoded.trim().split(".")
val mAdapt = moshi.adapter(JWTBody::class.java)
return mAdapt.fromJson(getJson(split[1])) ?: JWTBody()
}
#Throws(UnsupportedEncodingException::class)
private fun getJson(strEncoded: String): String {
val decodedBytes: ByteArray = Base64.decode(strEncoded, Base64.URL_SAFE)
return String(decodedBytes, charset("UTF-8"))
}
}
// JWT passed to the Utils
val decoded = JWTUtils.decoded(jwtString)

Kotlin multiple variables Null checker method implementation

I have multiple variables that can be nullable and i need to check them ( Strings and Dates ) .
I need a method where i pass it X number of variables and it returns me a list of the variables that are null.
I was thinking something that i can call like this :
internal fun checkNullVariables ( var x, var y , ..... ) : MutableList<String>{
// yada yada
return listOfNamesOfNullVariables
}
This definitely requires reflection, since you want parameter names. You need to add reflection as a dependency as explained in the documentation to use the below code.
private fun listNullProperties (vararg props: KProperty0<Any?>) : List<String> {
val list = mutableListOf<String>()
for (prop in props)
if (param.get() == null)
list.add(param.name)
return list
}
Usage:
val nullPropertiesByName = listNullParameters(
::myProperty,
::myOtherProperty,
::myDateProperty
)
println(nullPropertiesByName.joinToString())
If it is just about logging, you could:
fun <T> T?.logNull(name: String) {
when(this) {
null -> //log '$name' was null
else -> //do nothing
}
}
and call it like
var a: String? = null
a.logNull("my a variable") // "'my a variable' was null"
I still recommend the Map-approach. You may want to use properties stored in a map to overcome the use of reflection.
Here is an example using a type with 2 dates and 2 strings, both having a nullable and a non-null variant:
class YourData(internal val backedMap : Map<String, Any?>) {
val beginDate : Date by backedMap
val endDate : Date? by backedMap
val maybeString : String? by backedMap
val string : String by backedMap
constructor(beginDate : Date, string : String, endDate: Date? = null, maybeString : String? = null) : this(mapOf(
"beginDate" to beginDate,
"endDate" to endDate,
"maybeString" to maybeString,
"string" to string
))
}
While it may seem more complicated having that additional constructor in place, it just helps to easily create new objects the way you are most comfortable with.
Now you can either supply that function I placed in the comment or any variant of it. I now use an extension function for YourData instead:
fun YourData.getKeysWithNullValues() = backedMap.filterValues { it == null }.keys
Usage then may look as follows:
YourData(Date(), "test string")
.getKeysWithNullValues()
.forEach(::println)
which for this example would print:
endDate
maybeString

How to convert String to Int in Kotlin?

I am working on a console application in Kotlin where I accept multiple arguments in main() function
fun main(args: Array<String>) {
// validation & String to Integer conversion
}
I want to check whether the String is a valid integer and convert the same or else I have to throw some exception.
How can I resolve this?
You could call toInt() on your String instances:
fun main(args: Array<String>) {
for (str in args) {
try {
val parsedInt = str.toInt()
println("The parsed int is $parsedInt")
} catch (nfe: NumberFormatException) {
// not a valid int
}
}
}
Or toIntOrNull() as an alternative:
for (str in args) {
val parsedInt = str.toIntOrNull()
if (parsedInt != null) {
println("The parsed int is $parsedInt")
} else {
// not a valid int
}
}
If you don't care about the invalid values, then you could combine toIntOrNull() with the safe call operator and a scope function, for example:
for (str in args) {
str.toIntOrNull()?.let {
println("The parsed int is $it")
}
}
Actually, there are several ways:
Given:
// aString is the string that we want to convert to number
// defaultValue is the backup value (integer) we'll have in case of conversion failed
var aString: String = "aString"
var defaultValue : Int = defaultValue
Then we have:
Operation
Successful operation
Unsuccessful Operation
aString.toInt()
Numeric value
NumberFormatException
aString.toIntOrNull()
Numeric value
null
aString.toIntOrNull() ?: defaultValue
Numeric value
defaultValue
If aString is a valid integer, then we will get is numeric value, else, based on the function used, see a result in column Unsuccessful Operation.
val i = "42".toIntOrNull()
Keep in mind that the result is nullable as the name suggests.
As suggested above, use toIntOrNull().
Parses the string as an [Int] number and returns the result
or null if the string is not a valid representation of a number.
val a = "11".toIntOrNull() // 11
val b = "-11".toIntOrNull() // -11
val c = "11.7".toIntOrNull() // null
val d = "11.0".toIntOrNull() // null
val e = "abc".toIntOrNull() // null
val f = null?.toIntOrNull() // null
I use this util function:
fun safeInt(text: String, fallback: Int): Int {
return text.toIntOrNull() ?: fallback
}
In Kotlin:
Simply do that
val abc = try {stringNumber.toInt()}catch (e:Exception){0}
In catch block you can set default value for any case string is not converted to "Int".
string_name.toString().toInt()
converts string_name to String and then the resulting String is converted to int.
i would go with something like this.
import java.util.*
fun String?.asOptionalInt() = Optional.ofNullable(this).map { it.toIntOrNull() }
fun main(args: Array<String>) {
val intArgs = args.map {
it.asOptionalInt().orElseThrow {
IllegalArgumentException("cannot parse to int $it")
}
}
println(intArgs)
}
this is quite a nice way to do this, without introducing unsafe nullable values.
add (?) before fun toInt()
val number_int = str?.toInt()
You can Direct Change by using readLine()!!.toInt()
Example:
fun main(){
print("Enter the radius = ")
var r1 = readLine()!!.toInt()
var area = (3.14*r1*r1)
println("Area is $area")
}
fun getIntValueFromString(value : String): Int {
var returnValue = ""
value.forEach {
val item = it.toString().toIntOrNull()
if(item is Int){
returnValue += item.toString()
}
}
return returnValue.toInt()
}

Is there a way to use the default value on a non-optional parameter when null is passed?

For example, if I have the following data class:
data class Data(
val name: String = "",
val number: Long = 0
)
And functions that can return null:
fun newName(): String? {}
fun newNumber(): Long? {}
I know I can use the following to use the value of the functions if they are not null:
val newName = newName()
val newNumber = newNumber()
val data = Data(
if (newName != null) newName else "",
if (newNumber != null) newNumber else 0
)
But is there a way to just use the default value specified in the constructor of the Data class when the values are null?
I could not find anything in the documentation, but I was hoping something like this would work:
val data = Data(newName()?, newNumber()?)
But that does not compile.
You can define a companion object for your data class and overload its invoke operator to use default values when null is passed:
data class Data private constructor(
val name: String,
val number: Long
) {
companion object {
operator fun invoke(
name: String? = null,
number: Long? = null
) = Data(
name ?: "",
number ?: 0
)
}
}
the secondary constructor only supports for the Nullable primitive properties. which means it will result in 2 same constructors if the property is not a primitive type, for example:
data class Data(val name: String) {
constructor(name: String? = null) : this(name ?: "foo");
// ^--- report constructor signature error
}
data class Data(val number: Long = 0) {
constructor(number: Long? = null) : this(number ?: 0)
// ^--- No problem since there are 2 constructors generated:
// Data(long number) and Data(java.lang.Long number)
}
an alternative way is using invoke operator for that, for example:
data class Data(val name: String) {
companion object {
operator fun invoke(name: String? = null) = Data(name ?: "")
}
}
IF the class is not a data class, then you can lazy initializing properties from parameters, rather than define properties on the primary constructor, for example:
class Data(name: String? = null, number: Long? = null) {
val name = name ?: ""
val number = number ?: 0
}
If needed, I can offer another solution:
data class Data(
val inputName: String?,
val inputNumber: Long?
) {
private val name = inputName ?: ""
private val number = inputNumber ?: 0
}

Kotlin: how to pass an object function as parameter to another?

I am trying to learn functional Kotlin and have written this test code:
import java.util.*
data class BorrowerX(val name: String, val maxBooks: Int) {
companion object {
fun getName(br: BorrowerX): String = br.name
fun findBorrowerX(n: String, brs: ArrayList<BorrowerX>): BorrowerX? {
val coll: List<BorrowerX> = brs.filter { BorrowerX.getName(it) == n }
if (coll.isEmpty()) {
return null
} else return coll.first()
}
fun findBorrowerX2(n: String, brs: ArrayList<BorrowerX>, f: (BorrowerX) -> String): BorrowerX? {
val coll: List<BorrowerX> = brs.filter { f(it) == n }
if (coll.isEmpty()) {
return null
} else return coll.first()
}
}
}
In the REPL I can successfully call "findBorrowerX":
import BorrowerX
val br1 = BorrowerX(name = "Borrower1", maxBooks = 1)
val br2 = BorrowerX(name = "Borrower2", maxBooks = 2)
val br3 = BorrowerX(name = "Borrower3", maxBooks = 3)
val brs1 = arrayListOf(br1, br2, br3)
BorrowerX.findBorrowerX("Borrower1", brs1)
BorrowerX(name=Borrower1, maxBooks=1)
BorrowerX.findBorrowerX("Borrower-Bad", brs1)
null
But how do I make the call to "findBorrowerX2":
BorrowerX.findBorrowerX2("Borrower1", brs1, BorrowerX.getName(???))
And pass the iterated BorrowerX to getName??
This looks related, but I'm not sure:
Kotlin: how to pass a function as parameter to another?
Thank you in advance for your help with this!
EDIT:
Here is the equivalent Scala code for what I want to do:
def findBorrowerX2(n: String, brs: List[BorrowerX], f: BorrowerX => String): BorrowerX = {
val coll: List[BorrowerX] = brs.filter(f(_) == n)
if (coll.isEmpty) {
null
} else {
coll.head
}
}
scala> BorrowerX.findBorrowerX2("Borrower3", brs1, BorrowerX.getName(_))
res1: BorrowerX = BorrowerX(Borrower3,3)
scala> BorrowerX.findBorrowerX2("Borrower33", brs1, BorrowerX.getName(_))
res2: BorrowerX = null
Perhaps this is not possible in Kotlin?
You can use :: operator to get a function reference:
BorrowerX.findBorrowerX2("Borrower1", brs1, BorrowerX.Companion::getName)
Here BorrowerX.Companion::getName is a reference to the function getName declared in the companion object (named Companion) of the class BorrowerX. It has the type KFunction1<BorrowerX, String> which is a subtype of the required functional parameter type (BorrowerX) -> String.
It's worth noting that you can use :: operator to get a property reference too:
BorrowerX.findBorrowerX2("Borrower1", brs1, BorrowerX::name)
BorrowerX::name has the type KProperty1<BorrowerX, String> which also is a subtype of (BorrowerX) -> String. When invoked with the specified BorrowerX instance it returns the value of its name property.
As stated in the documentation on lambdas:
BorrowerX.findBorrowerX2("Borrower-Bad", brs1, { it.name })
or when the lambda is the last parameter of the method:
BorrowerX.findBorrowerX2("Borrower-Bad", brs1) { it.name }
Stating types and parameter names explicitly often improves readability:
BorrowerX.findBorrowerX2("Borrower-Bad", brs1) { borrower:BorrowerX -> borrower.name }