How to make a open class to sealed class in kotlin - kotlin

I have this below class, and I would like to make it a sealed class. How can I do it, and how to access this class from other classes?
open class Toy (var type: Type, var name : String) {
open val source: String = ""
}
I can make it sealed class as below but how to Instantiate it?
sealed class Toy (var type: Type, var name : String) {
open val source: String = ""
}

Related

Swift enum nested class VS Kotlin sealed class. Cant understand

I have code in Swift.
enum QuestionnaireViewMode {
case add(input: Add)
case edit(input: Edit)
enum Add {
case building(input: BuildingInput)
case car(input: CarInput)
case park
struct BuildingInput {
let address: String
let placeName: String
}
struct CarInput {
let name: String
}
}
enum Edit {
case profile(input: ProfileInput)
struct ProfileInput {
let name: String
}
}
}
This is enum class which very easy to use, for example i can create different type of object just like this: .add(input: .car(input: .init(name: "bmw"))). But for me not clear enum classes in kotlin, i found some similar - sealed class and i tryed converted to:
sealed class QuestionnaireViewMode {
sealed class add(input: Add)
sealed class edit(input: Edit)
sealed class Add {
sealed class building(input: BuildingInput)
sealed class car(input: CarInput)
sealed class park
data class BuildingInput(val address: String, val placeName: String)
data class CarInput(val name: String)
}
sealed class Edit {
sealed class profile(input: ProfileInput)
data class ProfileInput(val name: String)
}
}
is this correct?
Your sealed classes need to extend their parent. And you are using sealed class instead of fun for your builder functions, which doesn’t make sense. But since these are classes you don’t need that because you can call the constructors directly. Also, since you have no commonly shared state, these can be sealed interfaces instead of sealed classes, which is a little simpler because you don’t have to worry about constructors.
Here is how I would design it:
sealed interface QuestionnaireViewMode {
sealed interface Add: QuestionaireViewMode {
data class BuildingInput(val address: String, val placeName: String): Add
data class CarInput(val name: String): Add
}
sealed interface Edit: QuestionaireViewMode {
data class ProfileInput(val name: String): Edit
}
}
Usage:
val myInput: QuestionaireViewMode =
QuestionaireViewMode.Add.CarInput(“bmw”)

Map abstract type in Kotlin with MapStruct

I'm trying to map an abstract class with Mapstruct in Kotlin, but getting the following error:
AnimalMapper.java: error: The return type AnimalOutput is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.
My implementation:
#Mapper(componentModel = "jsr330")
interface AnimalMapper {
fun mapToDogOutput(dogInput: DogInput): DogOutput
fun mapToCatOutput(catInput: CatInput): CatOutput
fun mapToAnimalOutput(animalInput: AnimalInput): AnimalOutput {
when (animalInput) {
is DogInput -> mapToDogOutput(animalInput)
is CatInput -> mapToCatOutput(animalInput)
}
throw RuntimeException("Unsupported animal type");
}
}
sealed class AnimalInput {
abstract val name: String
}
data class CatInput(
override val name: String,
val catProperty: Int,
) : AnimalInput()
data class DogInput(
override val name: String,
val dogProperty: Float,
) : AnimalInput()
sealed class AnimalOutput {
abstract val name: String
}
data class CatOutput(
override val name: String,
val catProperty: Int,
) : AnimalOutput()
data class DogOutput(
override val name: String,
val dogProperty: Float,
) : AnimalOutput()
In my old java project, this corresponding implementation works as expected:
#Mapper(componentModel = "jsr330")
interface AnimalMapper {
DogOutput mapToDogOutput(DogInput dogInput);
CatOutput mapToCatOutput(CatInput catInput);
default AnimalOutput mapToAnimalOutput(AnimalInput animalInput) {
if (animalInput instanceof DogInput) {
return mapToDogOutput((DogInput) animalInput);
}
if (animalInput instanceof CatInput) {
return mapToCatOutput((CatInput) animalInput);
}
throw new RuntimeException("Unsupported animal type");
}
}
public abstract class AnimalInput {
public String name;
}
public abstract class CatInput extends AnimalInput {
public String name;
public int catProperty;
}
public abstract class DogInput extends AnimalInput {
public String name;
public float dogProperty;
}
public abstract class AnimalOutput {
public String name;
}
public abstract class CatOutput extends AnimalOutput {
public String name;
public int catProperty;
}
public abstract class DogOutput extends AnimalOutput {
public String name;
public float dogProperty;
}
I'm using the following dependency versions:
mapstructVersion: 1.4.2.Final
kotlinVersion: 1.5.21 (jvmTarget 16)
Anyone have an idea, how to fix this issue in my Kotlin project?
The reason why this is not working in Kotlin is due to the fact that the generated code by Kotlin marks the mapToAnimalOutput method as an abstract method.
You'll need to use #JvmDefault on that method in order for Kotlin to generate the correct modifiers for the method.

Duplicate fields in data classes that extend other (sealed) classes?

When a data class extends a sealed class containing a non-abstract open val property, the generated child data class contains private fields that duplicate the private fields of the parent class.
sealed class Foo(open val field1: String? = null)
data class Bar(override val field1: String? = null) : Foo(field1)
Output from javap -p Foo.class:
public abstract class com.example.Foo {
private final java.lang.String field1;
public java.lang.String getField1();
private com.example.Foo(java.lang.String);
com.example.Foo(java.lang.String, int, kotlin.jvm.internal.DefaultConstructorMarker);
public com.example.Foo(java.lang.String, kotlin.jvm.internal.DefaultConstructorMarker);
}
And javap -p Bar.class:
public final class com.example.Bar extends com.example.Foo {
private final java.lang.String field1;
public java.lang.String getField1();
public com.example.Bar(java.lang.String);
public com.example.Bar(java.lang.String, int, kotlin.jvm.internal.DefaultConstructorMarker);
public com.example.Bar();
public final java.lang.String component1();
public final com.example.Bar copy(java.lang.String);
public static com.example.Bar copy$default(com.example.Bar, java.lang.String, int, java.lang.Object);
public java.lang.String toString();
public int hashCode();
public boolean equals(java.lang.Object);
}
The bytecode for Bar.class contains its own private field field1; the field in the parent class does not appear to be re-used by the child class.
When using frameworks that set fields using reflection, which field will be set? Why is the field in the parent class not re-used by the child class? Is there a way to change the visibility of the field in the parent class to protected so it can be re-used by the child class?
In that caseBar holds the field indeed twice. Two alternatives to have a single field:
sealed class Foo(val field1: String?)
data class Bar(private val hiddenField1: String? = null) : Foo(hiddenField1)
or
sealed class Foo {
abstract val field1: String?
}
data class Bar(override val field1: String? = null) : Foo()
The field is not reused because you declared a separate property, which has its own backing field. If you want to reuse the field, change your code to:
sealed class Foo(val field1: String? = null)
data class Bar(field1: String? = null) : Foo(field1)
When using frameworks that set fields using reflection, which field will be set?
It depends on the class you use. Foo::class.java.getDeclaredField() or Bar::class.java.getDeclaredField().
See:
https://programming.guide/java/accessing-private-fields-of-superclass-through-reflection.html
What is the difference between getFields and getDeclaredFields in Java reflection
Why is the field in the parent class not re-used by the child class?
Why should it? You defined a field-backed property field1 in both classes. Both fields will exist but getField1() method is overridden by child class to return child class' field.
Is there a way to change the visibility of the field in the parent class to protected so it can be re-used by the child class?
Fields of lateinit properties have the same visibility as the getters. But I'm not sure that's what you want.
How about this?
sealed class Foo {
abstract val field1: String?
}
data class Bar(override val field1: String? = null) : Foo()
See discussion here: https://twitter.com/orangy/status/1033067930248867840

Kotlin : Interface with immutable property implemented by class with mutable

I have the following code:
Works around a limitation in Spring where #ConfigurationProperties classes need to follow the JavaBeans convention with mutable properties.
Instead of inject the mutable TokenConfigurationConfig we inject the interface.
public interface TokenAuthenticationConfig {
public fun apiKey() : String
}
#Component
#ConfigurationProperties(prefix = "service.api")
public open class TokenAuthenticationConfigImpl : TokenAuthenticationConfig
{
public var apiKey : String
constructor() {
this.apiKey = ""
}
override fun apiKey(): String
{
return this.apiKey
}
}
It works ok, but just wondering:
Is it possible in Kotlin to define an interface with an immutable property, that is implemented by class with a mutable property.
Uses of the interface would see the property as immutable, while users of the class would see it as mutable.
Yes, it definitely is possible to define such an interface and a class.
Any (publicly visible) property x in Kotlin means a pair of methods getX() and setX(..), generated by the compiler to satisfy the Java convention. That said, it is consistent that you can override getX in a class and add setX.
Here is an example:
interface SomethingImmutable {
val Somevar: String
}
class MyClass: SomethingImmutable {
override var Somevar: String = "Initial Value"
}

Swift framework nested classes are not exported well in Objective C

I have the following classes contained by Geography.framework (a Swift framework project):
public class Contact : NSObject
{
public static let Table: String = "contacts"
public class Fields : NSObject
{
public static let Id: String = "_id"
public static let Name: String = "name"
static let rawId: String = "rawId"
}
}
public class Country : NSObject
{
public class Fields : NSObject
{
public static let Id: String = "_id"
public static let Prefix: String = "prefix"
static let rawId: String = "rawId"
}
}
In my swift app using this framework everything works smoothly:
import geography
func setFields()
{
var contactName:String = Contact.Fields.Name
var countryPrefix:String = Country.Fields.Prefix
var contactsTable: String = Country.Table
}
Well, if I use the same Geography.framework in ObjectiveC, I see Contact and Country class but the nested classes Fields are not seen. Also the value of Contact.Table is not seen.
What I need to do in order to have same library structure and library usage in ObjectiveC?
Thank you,
You have to be explicit here with definition for ObjC.
public class Country: NSObject {
#objc(CountryFields) public class Fields: NSObject {
// ...
}
}
This should expose your Switf's Country.Fields for your ObjC as CountryFields. I haven't tested it but I believe you don't have to be explicit about inheriting from NSObject. #objc attribute should do it for you when compiling.
Update for Swift 3:
Looks like this was broken in Swift 3 and won't be fixed. https://bugs.swift.org/browse/SR-2267?focusedCommentId=21033&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-21033
You can use a trick with typealias to have the same syntax when using it :
public class Contact : NSObject
{
public static let Table: String = "contacts"
typealias Fields = ContactFields
}
#objcMembers
public class ContactFields : NSObject
{
public static let Id: String = "_id"
public static let Name: String = "name"
static let rawId: String = "rawId"
}
public class Country : NSObject
{
typealias Fields = CountryFields
}
#objcMembers
public class CountryFields : NSObject
{
public static let Id: String = "_id"
public static let Prefix: String = "prefix"
static let rawId: String = "rawId"
}