I have a few data classes, that is short, so I group them together in a file.
I can defined them in a Kotlin file as it is, but would prefer it to be scope within a class/object, so the file is not just a file, but under class/object for better grouping
I could do
object Model {
data class Result(val query: Query)
data class Query(val searchinfo: SearchInfo)
data class SearchInfo(val totalhits: Int)
}
and I could also do
class Model {
data class Result(val query: Query)
data class Query(val searchinfo: SearchInfo)
data class SearchInfo(val totalhits: Int)
}
They both looks the same to me. What's the different, and if there's a preferred way in term of scoping my data classes?
I would advise against using classes for scoping other classes. As Todd explains in his answer, you can use sealed classes which offer an actual benefit of exhaustive when checks. If you don't need this feature, Kotlin has a built-in mechanism for scoping - packages:
package org.company.model
data class Result(val query: Query)
data class Query(val searchinfo: SearchInfo)
data class SearchInfo(val totalhits: Int)
I can defined them in a Kotlin file as it is, but would prefer it to be scope within a class/object, so the file is not just a file, but under class/object for better grouping
There's nothing wrong with a file containing multiple top-level elements. This is a useful language feature and is used in exactly this kind of situation.
Another option is to make all of your data classes a subclass of a sealed Model class. This will give you the benefit of defining them all in one place, because Kotlin enforces that for sealed classs. Also, having the type system know about all instances of type Model is helpful in when expressions as well because then it won't require you to put an else -> block.
sealed class Model
data class Result(val query: Query) : Model()
data class Query(val searchinfo: SearchInfo) : Model()
data class SearchInfo(val totalhits: Int) : Model()
And you can just use them directly:
val r = Result(Query(SearchInfo(3))
Instead of wrapping them in another class or object, where you'd have to refer to them like this:
val r = Model.Result(Model.Query(Model.SearchInfo(3)))
Related
I need to (de)serialize to and from JSON some of the data classes that are generated by a Gradle plugin. Normally I would just use a library like Moshi or kotlinx.serialization and add the proper annotation to the class I want to serialize but, since this data classes are autogenerated, this is a problem.
I would like to avoid manually to map all the fields of the generated data class to some other class that I can (de)serialize, or to write a custom adapter for all these data class so, I was wondering if there is another way to tell, for example, kotlinx.serialization that a class is #Serializable without having to put the annotation directly on top of the class itself.
Or, alternatively, is there a better way to convert to and from a string an instance of a generated data class?
kotlinx.serialization supports generating serializers for 3rd party classes. We need to use forClass parameter in #Serializer, for example:
data class MyData(val data: String)
#Serializer(forClass = MyData::class)
object MyDataSerializer
fun main() {
val data = MyData("foo")
println(Json.encodeToString(MyDataSerializer, data))
}
You can read more in the official documentation: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#deriving-external-serializer-for-another-kotlin-class-experimental
I have a generic parent class:
open class Parent<T>{/*...*/}
and I have some derived classes that implement a specific instance of the generic parent:
class Derived1 : Parent<Foo1> {/*...*/}
class Derived2 : Parent<Foo2> {/*...*/}
where Foo1 and Foo2 are some classes defined elsewhere
I now need to create a function that returns a different derived class based on some input parameter:
fun getDerived(derived: SomeEnumType): Parent{
//return the correct derived class
}
Of course the line above won't compile because Parent requires a generic parameter. The Derived classes are not of the same type, so I wouldn't expect to be able to handle this polymorphically. How can I achieve this? I am familiar with kotlin.Any but this seems like cheating.
If it helps, I am using this pattern to parse json in the correct child class with the gson library (by overriding the deserializer)
You could get away with Parent<*> but if there is a relationship between Foo1 and Foo2 (e.g extending a common interface, Buzz) then you could use something like Parent<out Buzz>.
IIRC, <*> is like Java's wildcard <?>. Not bounding the response type will mean you'll need some type guards at the call site of your function getDerived to make the response inspectable.
I was going through Kotlin reference document and then I saw this.
The class declaration consists of the class name, the class header
(specifying its type parameters, the primary constructor etc.) and the
class body, surrounded by curly braces. Both the header and the body
are optional; if the class has no body, curly braces can be omitted.
class Empty
Now I'm wondering what is the use of such class declaration without header and body
Empty classes can be useful to represent state along with other classes, especially when part of a sealed class. Eg.
sealed class MyState {
class Empty : MyState()
class Loading : MyState()
data class Content(content: String) : MyState()
data class Error(error: Throwable) : MyState()
}
In this way you can think of them like java enum entries with more flexibility.
tldr: they want to demonstrate it's possible
even an empty class is of type Any and therefore has certain methods automatically. I think in most cases, this does not make sense, but in the documentation case it's used to show the simplest possible definition of a class.
The Java equivalent would be:
public final class Empty {
}
From practical programmer day to day perspective empty class makes no much sense indeed. There are however cases where this behavior is desirable.
There are scenarios where we want to make sure that we want to define a class and at the same time, we want to make sure that instance of this class will never be created (type created from such class is called empty type or uninhabited type).
Perfect example of this is Kotlin Nothing class with do not have class declaration header and body (notice that it also have private constructor)
https://github.com/JetBrains/kotlin/blob/master/core/builtins/native/kotlin/Nothing.kt
There are few usages for Nothing in Kotlin language. One of them would be a function that does not return a value (do not confuse this with Unit where the function returns actually returns a value of type Unit). A typical example is an assertFail method used for testing or method that exits current process. Both methods will never actually return any value yet we need to explicitly say tell it to a compiler using special type (Nothing).
fun assertFail():Nothing {
throw Exception()
}
Nothing can be also used with start projections where type Function<*, String> can be in-projected to Function<in Nothing, String>
Another usage for empty class is type token or placeholder:
class DatabaseColumnName
class DatabaseTableName
addItem(DatabaseColumnName.javaClass, "Age")
addItem(DatabaseTableName.javaClass, "Person")
...
getItemsByType(DatabaseTableName.javaClass)
Some languages are using empty classes for metaprogramming although I haven't explored this part personally:
Advantages of an empty class in C++
An example of empty class usage from Spring Boot framework:
#SpringBootApplication
class FooApplication
fun main(args: Array<String>) {
runApplication<FooApplication>(*args)
}
It doesn't make much sense as a final result. However it can be useful in active development and at a design time as a placeholder of some sort, which may be expanded in the future. Such terse syntax allows you to quickly define such new types as needed. Something like:
class Person (
val FirstName: String,
val LastName: String,
// TODO
val Address: Address
)
class Address
I think main reason this is specifically mentioned in documentation is to demonstrate, that language syntax in general can be terse, not that it is specifically created for common usage.
Sealed classes, in a sense, an extension of enum classes: the set of values for an enum type is also restricted, but each enum constant exists only as a single instance, whereas a subclass of a sealed class can have multiple instances which can contain state.
reference
Say I want to declare a simple algebraic datatype for integer lists:
sealed class IntList
data class Cons(val head: Int, val tail: IntList): IntList()
data class Nil() : IntList()
However, the last declaration results in an error
Data class must have at least one primary constructor parameter
Why is this limitation present? Looking at the documentation, there seems to be no good technical reasons for requiring data class constructors to be non-nullary.
Is it possible to express nullary constructors without having to write lots of boilerplate code? If I change the last declaration to something like
sealed class Nil() : IntList()
then I lose the free implementations of hashCode() and equals() that come for free with data class declarations.
EDIT
Alex Filatov gave a nice short solution below. Obviously, you never need more than one instance of Nil, so we can just define a singleton object
object Nil : IntList()
However, what would we do if our lists were parameterized by a type parameter? That is, now the first two lines of our definition would be
sealed class List<A>
data class Cons<A>(val head: A, val tail: List<A>): List<A>()
We cannot declare a polymorphic singleton Nil object which derives from List<A> for any A, since we have to provide a concrete type for A at the time of declaration. The solution (taken from this post) is to declare A as a covariant type parameter and declare Nil as a subtype of List<Nothing> as follows:
sealed class List<out A>
data class Cons<A>(val head: A, val tail: List<A>): List<A>()
object Nil : List<Nothing>()
This allows us to write
val xs: List<Int> = Cons(1, Cons(2, Nil))
val ys: List<Char> = Cons('a', Cons('b', Nil))
Because data class without data doesn't make sense. Use object for singletons:
object Nil : IntList()
You will have to create a usual class
class Nil : IntList()
and implement hashCode() and equals() yourself.
A data class without fields does not make any sense, since its job is to represent data.
Or: You can use an object class (as Alex Filatov said) which is a single instance class. Since, you don't need a state for each single Nil instance, they can share a single one.
I have a class which is the parse result of a string, so I have to enforce the toString() to return that source string instead of those parsed values. It also has custom equals()/hashCode() mechanism. Is there any benefit to still mark it as a data class?
The auto-generated parts of data classes are:
The compiler automatically derives the following members from all
properties declared in the primary constructor:
- equals()/hashCode() pair,
- toString() of the form "User(name=John, age=42)",
- componentN() functions corresponding to the properties in their order of declaration,
- copy() function.
If any of these functions is explicitly defined in the class body or
inherited from the base types, it will not be generated.
The componentN() function enables destructuring like for ((a, b, c) in dataClass) { ... }
However, data classes CANNOT be inherited. (You can define a data class that extends another non-data class though.)
If you think that it is possible that some classes extend your class, then do not make it a data class.
If you think that no class will extend your class in the future, and you maybe need the destruction or copy() function, then make it a data class.