What is the best way to apply platform specific annotations in Kotlin Multuplatform? - kotlin

I have some data transfer class which I want to share between platforms. There is only one difference. The implementations have different annotations on different platforms. What is the best way to do it? I know the only one way.
In the commonsMain:
expect class ErrorMessage (message: String = "") : DataTransferObject {
var message: String
}
In jvmMain:
#SomeJvmAnnotation
actual class ErrorMessage actual constructor (actual var message: String) : DataTransferObject
But if I implement every class this way than there is no profit from KMM. This way I need to implement every class n + 1 times where n is a number of platforms. Is there a simpler way to apply different annotations?
May be there is a way not to put expect on class.

Not the greatest solution, but you can use actual and expect to define your platform-specific annotations.
I used it to ignore unit tests only on the JS runtime.
commonMain
​/*​*
​ * Ignore a test when running the test on a JavaScript test runtime.
​ ​*/
​#Target( ​AnnotationTarget​.​CLASS​, ​AnnotationTarget​.​FUNCTION​ )
​expect​ ​annotation​ ​class​ ​JsIgnore​()
jsMain
​actual​ ​typealias​ ​JsIgnore​ ​=​ kotlin.test.​Ignore
jvmMain
​// Nothing to do. This should only ignore tests on the JavaScript test runtime.
​actual​ ​annotation​ ​class​ ​JsIgnore
I guess whether or not this is appropriate for you will really depend on the specific annotations you need for each platform, and to which degree they semantically overlap.

Related

Kotlin data class equality when one of the properties is a function

I wonder if a data class with one of the properties being a function, such as:
data class Holder(val x: Data, val f: () -> Unit)
can work at all, since the following test fails.
val a = {}
val b = {}
Assert.assertEquals(a, b)
Update: Use case for this could be to have a
data class ButtonDescriptor(val text: String, val onClick: () -> Unit)
and then flow it to UI whilst doing distinctUntilChanged()
I don't think this is possible, I'm afraid.
You can of course check reference equality (===, or == in this case because functions don't generally override equals()).  That would give you a definite answer where you have references to the same function instance.  But that doesn't check structural equality, and so reports the two lambdas in the question as different.
You can check whether the two functions are instances of the same class by checking their .javaClass property.  If the same, that would imply that they do the same processing — though I think they could still have different variables/captures.  However, if different, that wouldn't tell you anything.  Even the simple examples in the question are different classes…
And of course, you can't check them as ‘black boxes’ — it's not feasible to try every possible input and check their outputs.  (Even assuming they were pure functions with no side effects, which in general isn't true!)
You might be able to get their bytecode from a classloader, and compare that, but I really wouldn't recommend it — it'd be a lot of unnecessary work, you'd have to allow for the difference in class names etc., it would probably have a lot of false negatives, and again I think it could return the same code for two functions which behaved differently due to different parameters/captures.
So no, I don't think this is possible in JVM languages.
What are you trying to achieve with this, and could there be another way?  (For example, if these functions are under your control, can you arrange for reference equality to do what you need?  Or could you use function objects with an extra property giving an ID or something else you could compare?)
When you create your data class, if you pass the function by reference it will work with DiffUtils and distinctUntilChanged(). Function references do not break the isEquals() method of data classes in the same way that a lambda does.
For example, you create a function for your onClick:
private fun onClick() { // handle click }
and create your data class like
BottomDescriptor("some text", ::onClick)

Best way to define model using AndroidAnnotations #Rest and Kotlin?

I am totally new to Android and Kotlin and I was looking into Android Annotations.
I managed to decode a JSON response using the following code:
class ExampleModel {
#JvmField
final var id: Int = 0
lateinit var title: String
var description: String? = null
var author: Author? = null
}
#Rest(
rootUrl = "...",
converters = [MappingJackson2HttpMessageConverter::class]
)
interface ExampleClient {
#Get("/promotions")
fun getModels(): List<ExampleModel>
}
Now it does work but there are a couple of questions I'd like to ask.
Is it possible to use data classes? I tried but I kept getting an error from MappingJackson2HttpMessageConverter saying that there was no constructor available.
Is it somehow possible to just ignore extra keys that might appear in the JSON? Let's say that I am not interested in the author data for now, is there a way to just remove its declaration without having the decoding fail with "unexpected key"?
Consider that I usually work with Swift so if you could point me to the "Codable" equivalent in Kotlin I would really appreciate it.
Cheers
Kotlin Data classes don't have default constructor which is usually required by json deserialization libraries. Any data class require at least one constructor argument, but you can work around it. Define default values, you can use null. For example:
data class Pojo(val name: String? = null, val age: Int? = null)
Such code will allow to use Pojo() constructor. It should work, but it's better to use json deserializer that is more kotlin native or generate data classes with AutoValue.
Jackson that you're using here allows to ignore fields with #JsonIgnoreProperties.
If you're learning Android, don't start from Android Annotations if you don't have to. It's not very popular or modern solution. I used it in few projects back in the day, those were very difficult to maintain or to introduce new developers. Look into android architecture components and jetpack - google made few nice code labs. Also for json pick Moshi or Gson.

Kotlin benifits of writing helper/util methods without wrapping in class

There are can be two ways of writing helper method in Kotlin
First is
object Helper {
fun doSomething(a: Any, b: Any): Any {
// Do some businesss logic and return result
}
}
Or simply writing this
fun doSomething(a: Any, b: Any): Any {
// Do some businesss logic and return result
}
inside a Helper.kt class.
So my question is in terms of performance and maintainability which is better and why?
In general, your first choice should be top-level functions. If a function has a clear "primary" argument, you can make it even more idiomatic by extracting it as the receiver of an extension function.
The object is nothing more than a holder of the namespace of its member functions. If you find that you have several groups of functions that you want to categorize, you can create several objects for them so you can qualify the calls with the object's name. There's little beyond this going in their favor in this role.
object as a language feature makes a lot more sense when it implements a well-known interface.
There's a third and arguably more idiomatic way: extension functions.
fun Int.add(b: Int): Int = this + b
And to use it:
val x = 1
val y = x.add(3) // 4
val z = 1.add(3) // 4
In terms of maintainability, I find extension functions just as easy to maintain as top-level functions or helper classes. I'm not a big fan of helper classes because they end up acquiring a lot of cruft over time (things people swear we'll reuse but never do, oddball variants of what we already have for special use cases, etc).
In terms of performance, these are all going to resolve more or less the same way - statically. The Kotlin compiler is effectively going to compile all of these down to the same java code - a class with a static method.

What are sealed classes in Kotlin?

I'm a beginner in Kotlin and recently read about Sealed Classes. But from the doc the only think I actually get is that they are exist.
The doc stated, that they are "representing restricted class hierarchies". Besides that I found a statement that they are enums with superpower. Both aspects are actually not clear.
So can you help me with the following questions:
What are sealed classes and what is the idiomatic way of using ones?
Does such a concept present in other languages like Python, Groovy or C#?
UPDATE:
I carefully checked this blog post and still can't wrap my head around that concept. As stated in the post
Benefit
The feature allows us to define class hierarchies that are
restricted in their types, i.e. subclasses. Since all subclasses need
to be defined inside the file of the sealed class, there’s no chance
of unknown subclasses which the compiler doesn’t know about.
Why the compiler doesn't know about other subclasses defined in other files? Even IDE knows that. Just press Ctrl+Alt+B in IDEA on, for instance, List<> definition and all implementations will be shown even in other source files. If a subclass can be defined in some third-party framework, which not used in the application, why should we care about that?
Say you have a domain (your pets) where you know there is a definite enumeration (count) of types. For example, you have two and only two pets (which you will model with a class called MyPet). Meowsi is your cat and Fido is your dog.
Compare the following two implementations of that contrived example:
sealed class MyPet
class Meowsi : MyPet()
class Fido : MyPet()
Because you have used sealed classes, when you need to perform an action depending on the type of pet, then the possibilities of MyPet are exhausted in two and you can ascertain that the MyPet instance will be exactly one of the two options:
fun feed(myPet: MyPet): String {
return when(myPet) {
is Meowsi -> "Giving cat food to Meowsi!"
is Fido -> "Giving dog biscuit to Fido!"
}
}
If you don't use sealed classes, the possibilities are not exhausted in two and you need to include an else statement:
open class MyPet
class Meowsi : MyPet()
class Fido : MyPet()
fun feed(myPet: MyPet): String {
return when(myPet) {
is Mewosi -> "Giving cat food to Meowsi!"
is Fido -> "Giving dog biscuit to Fido!"
else -> "Giving food to someone else!" //else statement required or compiler error here
}
}
In other words, without sealed classes there is not exhaustion (complete coverage) of possibility.
Note that you could achieve exhaustion of possiblity with Java enum however these are not fully-fledged classes. For example, enum cannot be subclasses of another class, only implement an interface (thanks EpicPandaForce).
What is the use case for complete exhaustion of possibilities? To give an analogy, imagine you are on a tight budget and your feed is very precious and you want to ensure you don't end up feeding extra pets that are not part of your household.
Without the sealed class, someone else in your home/application could define a new MyPet:
class TweetiePie : MyPet() //a bird
And this unwanted pet would be fed by your feed method as it is included in the else statement:
else -> "Giving food to someone else!" //feeds any other subclass of MyPet including TweetiePie!
Likewise, in your program exhaustion of possibility is desirable because it reduces the number of states your application can be in and reduces the possibility of bugs occurring where you have a possible state where behaviour is poorly defined.
Hence the need for sealed classes.
Mandatory else
Note that you only get the mandatory else statement if when is used as an expression. As per the docs:
If [when] is used as an expression, the value of the satisfied branch becomes the value of the overall expression [... and] the else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions
This means you won't get the benefit of sealed classes for something like this):
fun feed(myPet: MyPet): Unit {
when(myPet) {
is Meowsi -> println("Giving cat food to Meowsi!") // not an expression so we can forget about Fido
}
}
To get exhaustion for this scenario, you would need to turn the statement into an expression with return type.
Some have suggested an extension function like this would help:
val <T> T.exhaustive: T
get() = this
Then you can do:
fun feed(myPet: MyPet): Unit {
when(myPet) {
is Meowsi -> println("Giving cat food to Meowsi!")
}.exhaustive // compiler error because we forgot about Fido
}
Others have suggested that an extension function pollutes the namespace and other workarounds (like compiler plugins) are required.
See here for more about this problem.
Sealed classes are easier to understand when you understand the kinds of problems they aim to solve. First I'll explain the problems, then I'll introduce the class hierarchies and the restricted class hierarchies step by step.
We'll take a simple example of an online delivery service where we use three possible states Preparing, Dispatched and Delivered to display the current status of an online order.
Problems
Tagged class
Here we use a single class for all the states. Enums are used as type markers. They are used for tagging the states Preparing, Dispatched and Delivered :
class DeliveryStatus(
val type: Type,
val trackingId: String? = null,
val receiversName: String? = null) {
enum class Type { PREPARING, DISPATCHED, DELIVERED }
}
The following function checks the state of the currently passed object with the help of enums and displays the respective status:
fun displayStatus(state: DeliveryStatus) = when (state.type) {
PREPARING -> print("Preparing for dispatch")
DISPATCHED -> print("Dispatched. Tracking ID: ${state.trackingId ?: "unavailable"}")
DELIVERED -> print("Delivered. Receiver's name: ${state.receiversName ?: "unavailable"}")
}
As you can see, we are able to display the different states properly. We also get to use exhaustive when expression, thanks to enums. But there are various problems with this pattern:
Multiple responsibilities
The class DeliveryStatus has multiple responsibilities of representing different states. So it can grow bigger, if we add more functions and properties for different states.
More properties than needed
An object has more properties than it actually needs in a particular state. For example, in the function above, we don't need any property for representing the Preparing state. The trackingId property is used only for the Dispatched state and the receiversName property is concerned only with the Delivered state. The same is true for functions. I haven't shown functions associated with states to keep the example small.
No guarantee of consistency
Since these unused properties can be set from unrelated states, it's hard to guarantee the consistency of a particular state. For example, one can set the receiversName property on the Preparing state. In that case, the Preparing will be an illegal state, because we can't have a receiver's name for the shipment that hasn't been delivered yet.
Need to handle null values
Since not all properties are used for all states, we have to keep the properties nullable. This means we also need to check for the nullability. In the displayStatus() function we check the nullability using the ?:(elvis) operator and show unavailable, if that property is null. This complicates our code and reduces readability. Also, due to the possibility of a nullable value, the guarantee for consistency is reduced further, because the null value of receiversName in Delivered is an illegal state.
Introducing Class Hierarchies
Unrestricted class hierarchy: abstract class
Instead of managing all the states in a single class, we separate the states in different classes. We create a class hierarchy from an abstract class so that we can use polymorphism in our displayStatus() function:
abstract class DeliveryStatus
object Preparing : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
class Delivered(val receiversName: String) : DeliveryStatus()
The trackingId is now only associated with the Dispatched state and receiversName is only associated with the Delivered state. This solves the problems of multiple responsibilities, unused properties, lack of state consistency and null values.
Our displayStatus() function now looks like the following:
fun displayStatus(state: DeliveryStatus) = when (state) {
is Preparing -> print("Preparing for dispatch")
is Dispatched -> print("Dispatched. Tracking ID: ${state.trackingId}")
is Delivered -> print("Delivered. Received by ${state.receiversName}")
else -> throw IllegalStateException("Unexpected state passed to the function.")
}
Since we got rid of null values, we can be sure that our properties will always have some values. So now we don't need to check for null values using the ?:(elvis) operator. This improves code readability.
So we solved all the problems mentioned in the tagged class section by introducing a class hierarchy. But the unrestricted class hierarchies have the following shortcomings:
Unrestricted Polymorphism
By unrestricted polymorphism I mean that our function displayStatus() can be passed a value of unlimited number of subclasses of the DeliveryStatus. This means we have to take care of the unexpected states in displayStatus(). For this, we throw an exception.
Need for the else branch
Due to unrestricted polymorphism, we need an else branch to decide what to do when an unexpected state is passed. If we use some default state instead of throwing an exception and then forget to take care of any newly added subclass, then that default state will be displayed instead of the state of the newly created subclass.
No exhaustive when expression
Since the subclasses of an abstract class can exist in different packages and compilation units, the compiler doesn't know all the possible subclasses of the abstract class. So it won't flag an error at compile time, if we forget to take care of any newly created subclasses in the when expression. In that case, only throwing an exception can help us. Unfortunately, we'll know about the newly created state only after the program crashes at runtime.
Sealed Classes to the Rescue
Restricted class hierarchy: sealed class
Using the sealed modifier on a class does two things:
It makes that class an abstract class. Since Kotlin 1.5, you can use a sealed interface too.
It makes it impossible to extend that class outside of that file. Since Kotlin 1.5 the same file restriction has been removed. Now the class can be extended in other files too but they need to be in the same compilation unit and in the same package as the sealed type.
sealed class DeliveryStatus
object Preparing : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
class Delivered(val receiversName: String) : DeliveryStatus()
Our displayStatus() function now looks cleaner:
fun displayStatus(state: DeliveryStatus) = when (state) {
is Preparing -> print("Preparing for Dispatch")
is Dispatched -> print("Dispatched. Tracking ID: ${state.trackingId}")
is Delivered -> print("Delivered. Received by ${state.receiversName}")
}
Sealed classes offer the following advantages:
Restricted Polymorphism
By passing an object of a sealed class to a function, you are also sealing that function, in a sense. For example, now our displayStatus() function is sealed to the limited forms of the state object, that is, it will either take Preparing, Dispatched or Delivered. Earlier it was able to take any subclass of DeliveryStatus. The sealed modifier has put a limit on polymorphism. As a result, we don't need to throw an exception from the displayStatus() function.
No need for the else branch
Due to restricted polymorphism, we don't need to worry about other possible subclasses of DeliveryStatus and throw an exception when our function receives an unexpected type. As a result, we don't need an else branch in the when expression.
Exhaustive when expression
Just like all the possible values of an enum class are contained inside the same class, all the possible subtypes of a sealed class are contained inside the same package and the same compilation unit. So, the compiler knows all the possible subclasses of this sealed class. This helps the compiler to make sure that we have covered(exhausted) all the possible subtypes in the when expression. And when we add a new subclass and forget to cover it in the when expression, it flags an error at compile time.
Note that in the latest Kotlin versions, your when is exhaustive for the when expressions as well the when statements.
Why in the same file?
The same file restriction has been removed since Kotlin 1.5. Now you can define the subclasses of the sealed class in different files but the files need to be in the same package and the same compilation unit. Before 1.5, the reason that all the subclasses of a sealed class needed to be in the same file was that it had to be compiled together with all of its subclasses for it to have a closed set of types. If the subclasses were allowed in other files, the build tools like Gradle would have to keep track of the relations of files and this would affect the performance of incremental compilation.
IDE feature: Add remaining branches
When you just type when (status) { } and press Alt + Enter, Enter, the IDE automatically generates all the possible branches for you like the following:
when (state) {
is Preparing -> TODO()
is Dispatched -> TODO()
is Delivered -> TODO()
}
In our small example there are just three branches but in a real project you could have hundreds of branches. So you save the effort of manually looking up which subclasses you have defined in different files and writing them in the when expression one by one in another file. Just use this IDE feature. Only the sealed modifier enables this.
That's it! Hope this helps you understand the essence of sealed classes.
If you've ever used an enum with an abstract method just so that you could do something like this:
public enum ResultTypes implements ResultServiceHolder {
RESULT_TYPE_ONE {
#Override
public ResultOneService getService() {
return serviceInitializer.getResultOneService();
}
},
RESULT_TYPE_TWO {
#Override
public ResultTwoService getService() {
return serviceInitializer.getResultTwoService();
}
},
RESULT_TYPE_THREE {
#Override
public ResultThreeService getService() {
return serviceInitializer.getResultThreeService();
}
};
When in reality what you wanted is this:
val service = when(resultType) {
RESULT_TYPE_ONE -> resultOneService,
RESULT_TYPE_TWO -> resultTwoService,
RESULT_TYPE_THREE -> resultThreeService
}
And you only made it an enum abstract method to receive compile time guarantee that you always handle this assignment in case a new enum type is added; then you'll love sealed classes because sealed classes used in assignments like that when statement receive a "when should be exhaustive" compilation error which forces you to handle all cases instead of accidentally only some of them.
So now you cannot end up with something like:
switch(...) {
case ...:
...
default:
throw new IllegalArgumentException("Unknown type: " + enum.name());
}
Also, enums cannot extend classes, only interfaces; while sealed classes can inherit fields from a base class. You can also create multiple instances of them (and you can technically use object if you need the subclass of the sealed class to be a singleton).

Generators for regular classes in scalacheck

In scalacheck's user guide there is "Generating Case Classes" paragraph. I modified example from it to use regular classes instead of case classes:
import org.scalacheck._
import Gen._
import Arbitrary._
sealed abstract class Tree
object Leaf extends Tree
class Node(left:Tree, rigth:Tree, v:Int) extends Tree
object Main {
val genLeaf = value(Leaf)
val genNode = for{
v <- Arbitrary.arbitrary[Int]
left <- genTree
rigth <- genTree
} yield new Node(left, rigth, v)
val genTree:Gen[Tree] = oneOf(genLeaf, genNode)
def main(args:Array[String]){
println(genTree.sample)
}
}
It seems everything working but I'm afraid of using this approach in production code before I ask here: is there any pitfalls?
This should work fine. There's nothing about case classes that are particularly magical about case classes as far as ScalaCheck is concerned. Any old class can get a generator, or even be convertible to Arbitrary.
As far as testing goes, one difference is that every non-case-class tree you generate will be unique, and thus for no two trees you generate will tree1 == tree2. That's different from how it is with case class, which test equality based on value rather than identity. You may need more tests to handle issues with aliasing that become possible when you have identity-based rather than value-based equality.
I see no problem here. The reason why case classes are used in that example is that the Tree shown is an algebraic data type, made possible with case classes. With normal classes, you cannot pattern match on the tree, and, in fact, you won't even be able to get left, right and v without declaring them val.