Is there better way for handle kotlinx serialization? - android

I use kotlinx.serialization on Kotlin native project, I a defined Super class for my models and all of the models extends from it.
I defined a function to called toJSON() for serialize variables and fields inside model that all of class models have it.
#Serializable
open class Model {
fun toJSON(): String = JSON.stringify(this);
}
And I created a subclass
class Me : Model() {
var name:String = "Jack";
}
but when I invoke JSON.stringify(this), IDE get a Warning to me:
This declaration is experimental and its usage must be marked with '#kotlinx.serialization.ImplicitReflectionSerializer' or '#UseExperimental(kotlinx.serialization.ImplicitReflectionSerializer::class)'
I paid attention and I used #ImplicitReflectionSerializer annotation while not worked.
Where is my problem?

This is discussed here. It's the particular overload you're using which is still experimental. So your options are either to use the other overload (which takes in a serializer) or to use one of the annotations mentioned in the error message. If you look at the answer to the question I linked (and the comments following it), you'll see it talks about using #UseExperimental and where it should be used.

Related

Kotlin - Issue Extending Kotlin String Class

I am currently trying to extend Kotlins String class with a method in a file StringExt.kt
fun String.removeNonAlphanumeric(s: String) = s.replace([^a-ZA-Z0-9].Regex(), "")
But Kotlin in not allowing me to use this method in a lambda:
s.split("\\s+".Regex())
.map(String::removeNonAlphanumeric)
.toList()
The error is:
Required: (TypeVariable(T)) -> TypeVariable(R)
Found: KFunction2<String,String,String>
What confuses me about this is that Kotlins Strings.kt has very similar methods and
I can call them by reference without Intellij raising this kind of issue. Any advice is appreciated.
This is because you have declared an extension function that accepts an additional parameter and should be used as s.replace("abc").
I think what you meant is the following:
fun String.removeNonAlphanumeric(): String = this.replace("[^a-ZA-Z0-9]".toRegex(), "")
This declaration doesn't have an extra parameter and uses this to refer to the String instance it is called on.
I thing this is because a lambda is an anonymous function and dose not access to the scope of a extension file.
Check this link maybe contains some useful information:
https://kotlinlang.org/docs/reference/extensions.html

Why does Google recommend using classes acting as functions for use cases?

Introduction
Semantically speaking, classes and interfaces act as nouns and methods/functions act as verbs. This is something that I recently read in the Java to Kotlin and it is aligned with how the vast majority of the people naturally name methods and classes.
For example we would expect a Car class to have a getBrand method, not a GetBrand class with an invoke method returning the brand of the car.
However, while reading the recent Guide to app architecture from Google, I have came across their naming convention for use cases, where they suggest this naming:
verb in present tense + noun/what (optional) + UseCase
with the following syntax to use it in Kotlin (example from here):
class FormatDateUseCase(userRepository: UserRepository) {
private val formatter = SimpleDateFormat(
userRepository.getPreferredDateFormat(),
userRepository.getPreferredLocale()
)
operator fun invoke(date: Date): String {
return formatter.format(date)
}
}
Question
Looking at the code above, we just have a class acting as a function. Why does Google recommend using these classes instead os just using top-level functions? Am I missing something here?
Your statement is not completely correct: We don't "have a class acting as a function", but you can say that each instance of the class acts as a function. That is important, since for instance, the constructor of FormatDataUseCase has a parameter of type UserRepository.
So, if we wanted to convert the FormatDataUseCase into a top-level function, it would not have the same signature as the invoke function of FormatDataUseCase, but it should also have userRepository as an additional parameter:
fun formatDateUseCase(userRepository: UserRepository, date: Date): String {
val formatter = SimpleDateFormat(
userRepository.getPreferredDateFormat(),
userRepository.getPreferredLocale()
)
return formatter.format(date)
}
And that is probably the main advantage of the recommended approach: You can have often-needed parameters and values automatically injected when you inject the use case class. This hides the dependencies from the caller of the use case, and makes it more comfortable to use.
Also, in the example the SimpleDateFormat is just initialized once and then reused for every call of the function, but if we converted it to a function, we would need to create a new instance on each call.

How to create interchangeable class types for android fake testing?

I'm trying to create a fake class for my repository to test a view model.
As far as I understood, the key element here is to create two classes with a common interface so both classes would contain the same methods.
The problem is I get a Type mismatch when trying to initialize an object.
I tried to do the same in a simplified manner:
class fakeClass1 : fakeInterface {
override fun getAllData(): String {
return ""
}}}
class fakeClass2 : fakeInterface {
override fun getAllData(): String {
return ""
}}
interface fakeInterface {
fun getAllData(): String}
val fakeClass: fakeClass1 = fakeClass2()
But that didn't work either.
What am I missing?
Ok, I figured it out.
I was wrong to think that those two classes should be interchangeable.
I solved it by making the ViewModel take the common interface in its constructor instead of the actual repository class. This allows the ViewModel to take any class which implement this interface as it's repository.
I think you worked it out, but just so you're clear (this is an important, fundamental thing!)
val fakeClass: fakeClass1 = fakeClass2()
This is defining a variable called fakeClass that refers to an object with the fakeClass1 type. Then you assign an object with the fakeClass2 type.
But a fakeClass2 is not a fakeClass1, neither is a superclass of the other, so you can't treat one as the other. Your example is simple, but imagine you added coolFunction() to fakeClass1 - they'd now happen to have different structures, and trying to call that method on an object that doesn't have it would cause a crash.
The only thing those classes have in common, is that they both have the fakeInterface type - they are fakeInterfaces, and that guarantees they implement the stuff in that interface (your getAllData function in this case). So if you treat them both as that type instead:
val fakeClass: fakeInterface = fakeClass2()
you can use either one, because they're both fakeInterfaces (similar to how Ints and Doubles are different but they're both Numbers). Because fakeClass is now a fakeInterface, you can only access the functions and properties that a fakeInterface has - you can't call coolFunction() even if you happened to pass in a fakeClass1, because fakeInterface doesn't have that.
(You could cast the variable to fakeClass1, basically saying "oh by the way this object is actually this type as well", but at that point the type system can't guarantee you're correct unless you're explicitly checking fakeClass is fakeClass1, and it'll warn you if that's the case)
The Java tutorials are pretty good and they'll give you an overview about how the types each form a kind of "contract" you work with

Populate list with instantiated classes dynamically with Kotlin on Android

I have an abstract class and all classes in some package derive from it. Is there a way to create a list that dynamically instantiates all these classes that reside in some package when using Kotlin on Android?
Here is an example:
com.example.service
BaseService
com.example.service.emailservice
GmailService
OutlookService
All classes in com.example.service.emailservice derive from BaseService abstract class that resides in com.example.service. I want to create a list that contains GmailService and OutlookService objects. I could instantiate them manually and add them to a list, but in future I may add new service, lets say YandexService, which should appear in list too. This requires manual instantiation again. Is there a way to automatically instantiate classes that reside in some package?
There are two mayor ways to do it.
The first one - easier and dirtier one called Reflection.
You may find a lot of examples in java. Not so much in Kotlin though, but from what I have read here it is more than possible.
Usage of Reflection though, is not recommended in production, and generally considered as a bad approach to fulfill something.
The second way is Annotation Processors - this way is way harder. It is considered a clean way to do such tasks though and it is a general standard for code generating techniques. This way you can do all kinds of magic if you put your mind to it. Here is nice article about how to do it.
Generally I would recommend to use semiautomatic approach.
For example:
In your base service with init method
abstract class BaseService {
abstract fun init(): BaseService
}
Make all your services implement this BaseService
class Service : BaseService() {
override fun init(): BaseService {
return Service()
}
}
...
And then just create a list of classes
val services = listOf(Service(), Service1(), Service2())
and to init them do
services.forEach { it.init() }
This is not very different from what you have and may require some logical and architectural changes in your Services and App overall, but it won't be dirty and it won't require tremendous learning curve and time expenses.
Hope it helps.
use this library
add to gradle.build
implementation 'org.reflections:reflections:0.9.11'
then in your package com.example.service create a class from where you will be creating your Services, let's call it ServiceFactory here is implementation:
package com.example.service
import org.reflections.Reflections
class ServiceFactory {
init {
val reflections = Reflections(javaClass.`package`.name)
val subTypes = reflections.getSubTypesOf(BaseService::class.java)
val yourServices = subTypes.map { it.getConstructor().newInstance() }
yourServices.forEach { println(it.javaClass.name) }
}
}
fun main() {
ServiceFactory()
}
out:
com.example.service.emailservice.OutlookService
com.example.service.emailservice.GmailService
Something you were looking for ?
EDIT: of course you can instantiate this services from different place, but in that case you need to hardcode Reflections("com.example.service")

Does Kotlin's "#Parcelize" work with non data classes?

I have simple data classes. I mean, they are data classes logically, but not data class, because I need inheritance and other constructors. They only have fields (of basic types Int?, String?, or List<String>?, etc), and constructors.
I need to pass them (all of their fields need to be passed) from Activity to Activity, so I need to make them parcellisable (or is there a better way?). I first created them as data class and just added #Parcelize. Even though there was a warning and red line that said "CREATOR" or something, I could ignore them and the app worked as intended.
But, now for the reasons above, I changed them to normal classes, and suddenly there is a compilation error.
Error:java.util.NoSuchElementException: Collection contains no element matching the predicate.
at org.jetbrains.kotlin.android.parcel.ParcelableCodegenExtension.getPropertiesToSerialize(ParcelableCodegenExtension.kt:374)
....too long...
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Error:Execution failed for task ':app:kaptGenerateStubsDebugKotlin'. Internal compiler error. See log for more details
How can I solve this problem? Should I use data class? But I need to parse a JSON object to create them. The classes look like these (not actual classes, but simplified for illustration purposes). Is there a better way than implementing that boring, bulky parcellable code by hand?
#Parcelize
open class Dog():Parcelable
{
var someField1;
var someField2;
constructor(data:JSON):this()
{
//parse data to set the fields.
}
}
#Parcelize
class Doge:Dog
{
var someField3;
var someField4;
constructor():super(); //I do not use it, but for parcellable
constructor(data:JSON):super(data)
{
//parse data to set the fields.
}
}
PS. I had to switch to PaperParcel. It was very similar to Kotlin's, but it did not require a primary constructor. It only required the same thing to be any constructor, so I could just create a secondary constructor with the same argument names as those of fields, and it worked. Although, I wonder why the CREATOR could not be created automatically.
For example,
#PaperParcel
class Doge:Dog
{
var someField3;
var someField4;
//Needed only for PaperParcel
constructor(someField3, someField4)
{
this.someField3 = someField3;
this.someField4 = someField4;
}
companion object
{
#JvmField val CREATOR = PaperParcelDoge.CREATOR
}
//end of PaperParcel stuff.
constructor(data:JSON):super(data)
{
//parse data to set the fields.
}
}
As stated here, your properties should be declared inside your primary constructor.
Parcelable support
Android Extensions plugin now includes an automatic
Parcelable implementation generator. Declare the serialized properties
in a primary constructor and add a #Parcelize annotation, and
writeToParcel()/createFromParcel() methods will be created
automatically:
#Parcelize
class User(val firstName: String, val lastName: String) : Parcelable

Categories

Resources