Kotlin-ekvivalenten till Omit TypeScript-verktygstyp
Jag har två dataklasser i Kotlin som är väldigt lika. Den enda skillnaden är att den ena har ett ID-fält och den andra inte har det (ID:t genereras endast om modellen har lagrats i databasen).
data class RouteWithId(
val id: String,
val name: String,
val description: String,
val comments: List<Comment>,
val media: List<Media>,
val points: List<RoutePoint>,
val userId: String,
val status: RouteState,
val tracks: List<TrackInfo>,
) {
enum class RouteState(val value: String){
IN_REVIEW("in-review"),
PUBLISHED("published");
}
}
data class Route(
val name: String,
val description: String,
val comments: List<Comment>,
val media: List<Media>,
val points: List<RoutePoint>,
val userId: String,
val status: RouteState,
val tracks: List<TrackInfo>,
) {
enum class RouteState(val value: String){
IN_REVIEW("in-review"),
PUBLISHED("published");
}
}
Jag har försökt att förena dem med ett nollbart id-fält, men många gånger skapar det bara mycket brus eftersom jag vet att i flera fall skulle ID:t finnas (vilken som helst efterbearbetning efter att ha laddats från databasen).
Jag har sett att TypeScript har en hjälptyp som heter Omit, som kan användas för att härleda en typ från en annan typ men utelämna vissa fält.
Detta skulle vara perfekt för mitt användningsfall, eftersom jag vet att efter att ha laddat från en databas kommer fältet att finnas där, och innan dess vet jag också att det inte kommer att finnas där.
Vet du hur man uppnår samma sak i Kotlin?
Svar #1
Det finns inga unionstyper i Kotlin, men du kan använda arv för att uppnå detta:
class Route(val name: String, val description: String, /* ... */) {
enum class RouteState(val value: String){
IN_REVIEW("in-review"),
PUBLISHED("published");
}
fun withId(id: Int): DBRoute = DBRoute(id, name, description, /* ... */)
}
class DBRoute(val id: Int, name: String, description: String, /* ... */) : Route(name, description) {
fun asRoute(): Route = Route(name, description, /* ... */)
}
På detta sätt kan du konvertera en Route till en DBRoute och vice versa. Nackdelen med detta är att du måste skriva in alla fälten från Route i konstruktorn för DBRoute.
Jag vet inte om det skulle vara bra för dig, men du skulle också kunna använda en Map<Int, Route> utan att behöva skapa en ny klass, men detta passar bättre för en klass om du behöver några operationer som på något sätt involverar ID:t.
Svar #2
TLDR: Du kan inte uppnå det du vill "på rätt sätt" i ett objektorienterat språk som Kotlin.
Svar :
Först, låt oss sammanfatta en av principerna för ett objektorienterat språk: Arv. (Se här för fullständig lista över principerna)
Arv. Relationer och underklasser mellan objekt kan tilldelas, vilket gör det möjligt för utvecklare att återanvända gemensam logik samtidigt som en unik hierarki bevaras. Denna egenskap hos OOP tvingar fram en mer noggrann dataanalys, minskar utvecklingstiden och säkerställer en högre grad av noggrannhet.
Så enligt definitionen av arv ges inte underklassen ett alternativ att VÄLJA vad den ska ärva. Det betyder att den måste ärva alla egenskaper från sin basklass; den kan inte vara kräsen med vad den ärver.
Till exempel, om basklassen har en Lung Cancer-egenskap, MÅSTE barnklassen också ha Lung Cancer-egenskapen. Barnklassen kan inte säga "Eh, Lung Cancer är dåligt, så jag vill inte ärva det!".
För det andra, låt oss hänvisa till definitionen av Data class i Kotlin. (Definition av Data Class) Kotlin har satt en mycket strikt regel i Data class så att "En dataklass FÅR INTE ha en underklass eller en överklass". Det innebär att arv inte är lämpligt när du använder Data Class. Tyvärr, om du vill utföra arv i Kotlin, kan du inte använda Data Class, använd bara en vanlig klass.
Men om du verkligen måste göra det, kan du "fuska" genom OOP och använda abstract och interface-klasser i Kotlin. Tydligen nog är detta en riktigt dålig praxis inom OOP. Jag skulle råda dig att tänka om på dina modelleringssystem igen. (Har du övervägt att använda Optional i Kotlin?)
Här är mitt exempel för dig:
//create an interface class that 'enforces ID'
interface HasID {
val id: Int
}
//Data Class must have ID
data class RouteWithId(val length: Int, override val id: Int) : HasID
//Data Class without ID
data class RouteWithOutId(val name: String)
Låt mig veta om du har några fler frågor. Tack
