4 ms·
The article does not actually explain how to do inheritance. In case you are wondering, the answer is using sealed classes. Some neat things with data clases (
by jillesvangurp 4y ago
The article does not actually explain how to do inheritance. In case you are wondering, the answer is using sealed classes.
Some neat things with data clases (vs. e.g. Java's records):
- default values for parameters
- if you use vals, your data classes are immutable
- copy constructor that has the same parameters as the constructor with their current values as the default value.
- destructuring values
- extension and delegated properties. Those are not limited to data classes of course but they are really nice to use with data classes.
Kotlin multi platform and data classes are actually a nice combination btw. We have a spring boot project with a Kotlin multi platform API client that we can consume on Android, IOS, in a Browser and in our integration tests for the server. Exact same code working exactly the same way on IOS native, in a browser, or on Android and the JVM. It uses ktor client for http communication, and kotlinx serialization for json parsing. Works great.
- elijahmoses 4y ago[dead]
- Xeophon 4y agoKotlin is simply amazing if you pair it with the corresponding frameworks, i.e. kotlinx serialization instead of gson/jackson etc. Quickly fell in love with the language and Java, even in the new(er) versions, feels outdated.
- pron 4y agoThe reason Java rejected data classes in favour of the very different and more powerful approach of nominal tuples (i.e. records) and algebraic data types in general, was the observation that the vast majority of data classes can be record classes. It's by not trying to cover the remaining small minority of cases with the same construct that we gain the guarantees of a more powerful one based on the property that the full state of a record is described by its canonical constructor and its deconstruction serves as its dual. All records are well-behaved as collection members; not all data classes are. All records can be safely serialised and deserialised; not all data classes can. All records can be deconstructed and reconstructed; not all data classes can. Data classes are about declaring classes with less code. When you have an object of a data class you know nothing more about its behaviour than you do about an object of any class. Record classes, which are similar in their design philosophy to enums, are not about writing less code, but about being reifying the concept of unencapsulated data, which offers semantic guarantees -- not just less syntax -- about the nature of the objects, just as you can with enums. Java is able to offer new constructs such as records and enums, with runtime support, as it's not a hosted language.
- scns 4y agoLike your input a lot. > All records are well-behaved as collection members; not all data classes are. > All records can be safely serialised and deserialised; not all data classes can. > All records can be deconstructed and reconstructed; not all data classes can. Can you give examples please?
- pron 4y ago> All records are well-behaved as collection members; not all data classes are. This is due to immutability. A var in a data class constructor means that the object's hashCode may change after its construction, and so mutating such an object may break collections that rely on hashCode to locate members, such as HashMaps and HashSets. > All records can be safely serialised and deserialised; not all data classes can. This is because records do not have any state beyond that which is constructed by the canonical constructor, which is never bypassed. When serializing a record, its components are serialized. When deserializing one, the only way to construct the record is by passing the deserialized components to the canonical construct, which then validates them. Deserializing an object with hidden state bypasses the constructor and can inject inconsistent, potentially harmful, data into it. > All records can be deconstructed and reconstructed; not all data classes can. A record is its components (and type; this is why they're nominal tuples). When you deconstruct a record with a pattern, you get its full state. If you put it back together you'll get an object with the same data as the original.
- ohgodplsno 4y ago>A var in a data class constructor Just because you can, doesn't mean you should. I can count on one hand the number of data classes with vars in our codebase of 500k+LOC, and they are specialized implementations for processes that would otherwise entail lots of copying and awful performance, whereas mutation just works.
- pron 4y agoThat is the exact observation I was referring to! You can make that desired behaviour into a guarantee that the runtime and any other code can rely on without giving up much. So even though most data classes could be records, the construct still doesn't make the necessary guarantees. Because those times where data objects require mutation are not numerous, the benefit of reducing their syntax is small. So Java was able to give the syntax benefits in almost all of the same cases, and additionally it was able to make powerful behaviour guarantees.
- TeeWEE 4y agoHow do you deal with schema versioning? We use the exact same setup. However older client version often trip over server schema changes. While this is a generic problem, it’s harder to prevent when your models it’s not built into your network data contract. That’s why we considering using grpc which has backwards compatibility support on its design. Things can still break if you delete endpoints, but at least it’s contained to the idl file.
- jillesvangurp 4y agoI basically don't version schemas currently. We do version our library of course. So you have to opt into using a new version. But managing compatibility breaking API changes is a bit of an issue of course. Not just for Kotlin. I mitigate it by configuring the serializer to be tolerant of new or missing keys, enum values, etc. The defaults for this are actually a bit dangerous/wrong IMHO. Also, most new properties that we add are nullable and default to null. This way we can safely deal with values not being there. And we use deprecations before removing anything from the API (which are nice in kotlin because you can automatically fix them in intellij with replace patterns). There are a few other things you can do. Like implementing some good contract tests, etc. We actually considered grpc at some point but decided against it as being a bit too limiting and mobile centric at the time. Also, grpc in a browser was not well supported at the time we looked. Maybe that has changed?
- TeeWEE 4y ago> I mitigate it by configuring the serializer to be tolerant of new or missing keys, enum values, et Good point, we do that too. > We actually considered grpc at some point but decided against it as being a bit too limiting and mobile centric at the time. Also, grpc in a browser was not well supported at the time we looked. Maybe that has changed? Its supported: https://github.com/grpc/grpc-web https://github.com/grpc/grpc-web Transcoding to HTTP is also supported: https://cloud.google.com/endpoints/docs/grpc/transcoding https://cloud.google.com/endpoints/docs/grpc/transcoding https://cloud.google.com/blog/products/api-management/understanding-grpc-openapi-and-rest-and-when-to-use-them https://cloud.google.com/blog/products/api-management/unders...