कोटलिन में डेटा क्लास बढ़ाएँ


176

डेटा कक्षाएं जावा में पुराने जमाने के पीओजेओ के प्रतिस्थापन के रूप में प्रतीत होती हैं। यह काफी उम्मीद है कि ये वर्ग विरासत के लिए अनुमति देंगे, लेकिन मैं डेटा वर्ग का विस्तार करने के लिए कोई सुविधाजनक तरीका नहीं देख सकता। मुझे कुछ इस तरह की आवश्यकता है:

open data class Resource (var id: Long = 0, var location: String = "")
data class Book (var isbn: String) : Resource()

component1()विधियों के टकराव के कारण उपरोक्त कोड विफल हो जाता है । dataकेवल एक वर्ग में एनोटेशन छोड़ने से काम नहीं होता है।

शायद डेटा वर्गों का विस्तार करने के लिए एक और मुहावरा है?

UPD: मैं केवल चाइल्ड चाइल्ड क्लास का एनोटेट कर सकता हूं, लेकिन dataएनोटेशन केवल कंस्ट्रक्टर में घोषित संपत्तियों को संभालता है। यही है, मुझे सभी माता-पिता के गुणों की घोषणा करनी होगी openऔर उन्हें ओवरराइड करना होगा, जो बदसूरत है:

open class Resource (open var id: Long = 0, open var location: String = "")
data class Book (
    override var id: Long = 0,
    override var location: String = "",
    var isbn: String
) : Resource()

3
कोटलिन ने स्पष्ट रूप से ऐसे तरीकों का निर्माण किया है componentN()जो एन-वें संपत्ति का मूल्य लौटाते हैं। मल्टी-डिक्लेरेशन
दिमित्री

गुणों को खोलने के लिए, आप संसाधन सार बना सकते हैं या संकलक प्लगइन का उपयोग कर सकते हैं। कोटलिन खुले / बंद सिद्धांत के बारे में सख्त है।
Željko Trogrlić

@Dmitry चूंकि हम एक डेटा क्लास का विस्तार नहीं कर सकते हैं, क्या आपका "समाधान" पेरेंट क्लास वेरिएबल को खुला रखने और उन्हें केवल चाइल्ड क्लास में "ओके" काम के दौरान ओवरराइड करना होगा?
आर्ची जी Quiñones

जवाबों:


163

सच्चाई यह है: डेटा कक्षाएं वंशानुक्रम के साथ बहुत अच्छी तरह से नहीं खेलती हैं। हम डेटा वर्गों की विरासत को प्रतिबंधित या गंभीर रूप से प्रतिबंधित करने पर विचार कर रहे हैं। उदाहरण के लिए, यह ज्ञात है कि equals()गैर-अमूर्त वर्गों पर पदानुक्रम में सही तरीके से लागू करने का कोई तरीका नहीं है ।

इसलिए, सभी मैं पेशकश कर सकता हूं: डेटा वर्गों के साथ विरासत का उपयोग न करें।


अरे एंड्री, यह कैसे () के बराबर है क्योंकि यह डेटा वर्गों पर उत्पन्न होता है अब काम करता है? क्या यह केवल मेल खाता है यदि प्रकार सटीक है और सभी सामान्य फ़ील्ड समान हैं, या केवल यदि फ़ील्ड समान हैं? ऐसा लगता है, क्योंकि बीजीय डेटा प्रकारों को सन्निकटन करने के लिए वर्ग की विरासत के मूल्य के कारण, यह इस समस्या के समाधान के साथ आने के लायक हो सकता है। दिलचस्प बात यह है कि एक सरसरी खोज ने मार्टिन ओडस्की के विषय पर इस चर्चा का खुलासा किया: artima.com/lejava/articles/equality.html
orospakr

3
मुझे विश्वास नहीं है कि इस समस्या का एक समाधान है। अब तक मेरी राय यह है कि डेटा कक्षाओं में डेटा-सबक्लेसेस नहीं होने चाहिए।
एंड्री ब्रेज़लव

3
क्या होगा यदि हमारे पास कुछ ORM जैसे एक पुस्तकालय कोड है और हम अपने लगातार डेटा मॉडल के लिए इसके मॉडल का विस्तार करना चाहते हैं?
कृपाल शाह

3
डेटा क्लासेस पर @AndreyBreslav डॉक्स कोटलिन 1.1 के बाद की स्थिति को नहीं दर्शाता है। 1.1 से डेटा कक्षाएं और वंशानुक्रम एक साथ कैसे खेलते हैं?
यूजेन पिंचेक

2
@EugenPechanec इस उदाहरण को देखें: kotlinlang.org/docs/reference/…
Andrey Breslav

114

निर्माण के बाहर सुपर क्लास में संपत्तियों को सार के रूप में घोषित करें, और उन्हें उप-वर्ग में ओवरराइड करें।

abstract class Resource {
    abstract var id: Long
    abstract var location: String
}

data class Book (
    override var id: Long = 0,
    override var location: String = "",
    var isbn: String
) : Resource()

15
यह सबसे अधिक लचीला प्रतीत होता है। मुझे प्यारी इच्छा है कि हमारे पास बस एक दूसरे से विरासत में मिले डेटा वर्ग हो सकते हैं ...
एडम

हेलो सर, डाटा क्लास इनहेरिटेंस को संभालने के सबसे अच्छे तरीके के लिए धन्यवाद। जब मैं अमूर्त वर्ग को जेनेरिक प्रकार के रूप में उपयोग करता हूं तो मुझे एक समस्या का सामना करना पड़ रहा है। मुझे एक Type Mismatchत्रुटि मिली: "आवश्यक टी, मिला: संसाधन"। क्या आप मुझे बता सकते हैं कि इसे जेनरिक में कैसे इस्तेमाल किया जा सकता है?
अश्विन महाजन

मैं यह भी जानना चाहूंगा कि क्या अमूर्त वर्गों में जेनरिक संभव है। उदाहरण के लिए, यदि स्थान एक विरासत में मिला डेटा श्रेणी और एक कस्टम वर्ग में है ( Location(long: Double, lat: Double))दूसरे में कहते हैं ?
रॉबी क्रोनिन

2
मैंने अपनी उम्मीद लगभग खो दी है। धन्यवाद!
मिशैल पॉल्कोका

मापदंडों को डुप्लिकेट करना विरासत को लागू करने का एक खराब तरीका लगता है। तकनीकी रूप से, चूंकि बुक संसाधन से विरासत में मिली है, इसलिए यह जानना चाहिए कि आईडी और स्थान मौजूद है। वास्तव में उन लोगों को निर्दिष्ट करने की आवश्यकता नहीं होनी चाहिए।
AndroidDev

23

अमूर्त वर्ग का उपयोग करने से ऊपर समाधान वास्तव में इसी वर्ग उत्पन्न करता है और डेटा वर्ग को इससे निकालता है।

यदि आप सार वर्ग को पसंद नहीं करते हैं, तो इंटरफ़ेस का उपयोग कैसे करें ?

कोटलिन में इंटरफ़ेस के गुण हो सकते हैं जैसा कि इस लेख में दिखाया गया है ।

interface History {
    val date: LocalDateTime
    val name: String
    val value: Int
}

data class FixedHistory(override val date: LocalDateTime,
                        override val name: String,
                        override val value: Int,
                        val fixedEvent: String) : History

मैं उत्सुक था कि कैसे कोटलिन ने इसे संकलित किया। यहाँ बराबर Java कोड (Intellij [Kotlin bytecode] सुविधा का उपयोग करके उत्पन्न):

public interface History {
   @NotNull
   LocalDateTime getDate();

   @NotNull
   String getName();

   int getValue();
}

public final class FixedHistory implements History {
   @NotNull
   private final LocalDateTime date;
   @NotNull
   private final String name;
   private int value;
   @NotNull
   private final String fixedEvent;

   // Boring getters/setters as usual..
   // copy(), toString(), equals(), hashCode(), ...
}

जैसा कि आप देख सकते हैं, यह बिल्कुल सामान्य डेटा क्लास की तरह काम करता है!


3
दुर्भाग्य से डेटा वर्ग के लिए इंटरफ़ेस पैटर्न लागू करना कक्ष की वास्तुकला के साथ काम नहीं करता है।
एडम हर्विट्ज

@AdamHurwitz यह बहुत बुरा है .. मैंने उस पर ध्यान नहीं दिया!
तुरा

4

@ Correcteljko Trogrlić का उत्तर सही है। लेकिन हमें उन्हीं क्षेत्रों को दोहराना होगा जैसे कि एक अमूर्त वर्ग में।

इसके अलावा, अगर हम सार वर्ग के अंदर सार उपवर्ग हैं , तो एक डेटा वर्ग में हम इन सार उपवर्गों से फ़ील्ड का विस्तार नहीं कर सकते हैं। हमें पहले डेटा उपवर्ग बनाना चाहिए और फिर फ़ील्ड्स को परिभाषित करना चाहिए ।

abstract class AbstractClass {
    abstract val code: Int
    abstract val url: String?
    abstract val errors: Errors?

    abstract class Errors {
        abstract val messages: List<String>?
    }
}



data class History(
    val data: String?,

    override val code: Int,
    override val url: String?,
    // Do not extend from AbstractClass.Errors here, but Kotlin allows it.
    override val errors: Errors?
) : AbstractClass() {

    // Extend a data class here, then you can use it for 'errors' field.
    data class Errors(
        override val messages: List<String>?
    ) : AbstractClass.Errors()
}

हम History.Errors को AbstractClass.Errors.Companion.SimpleErrors या बाहर ले जा सकते हैं और डेटा वर्ग में प्रत्येक इनहेरिट करने वाले डेटा वर्ग पर इसे डुप्लिकेट करने के बजाय उपयोग कर सकते हैं?
TWStStrrob

@TWiStErRob, ऐसे प्रसिद्ध व्यक्ति को सुनकर खुशी हुई! मेरा मतलब था कि History.Errors हर वर्ग में बदल सकता है, इसलिए हमें इसे ओवरराइड करना चाहिए (उदाहरण के लिए, फ़ील्ड जोड़ें)।
कूलमाइंड

4

Kotlin Traits मदद कर सकता है।

interface IBase {
    val prop:String
}

interface IDerived : IBase {
    val derived_prop:String
}

डेटा कक्षाएं

data class Base(override val prop:String) : IBase

data class Derived(override val derived_prop:String,
                   private val base:IBase) :  IDerived, IBase by base

नमूना उपयोग

val b = Base("base")
val d = Derived("derived", b)

print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"

यह दृष्टिकोण @Parcelize के साथ विरासत के मुद्दों के लिए एक समाधान भी हो सकता है

@Parcelize 
data class Base(override val prop:Any) : IBase, Parcelable

@Parcelize // works fine
data class Derived(override val derived_prop:Any,
                   private val base:IBase) : IBase by base, IDerived, Parcelable

2

आप एक गैर-डेटा वर्ग से एक डेटा वर्ग प्राप्त कर सकते हैं। किसी अन्य डेटा वर्ग से डेटा क्लास इनहेरिट करने की अनुमति नहीं है क्योंकि कंपाइलर-जनरेट किए गए डेटा क्लास मेथड को वंशानुक्रम के मामले में लगातार और सहज ज्ञान युक्त तरीके से काम करने का कोई तरीका नहीं है।


1

equals()एक पदानुक्रम में सही ढंग से लागू करते समय वास्तव में काफी अचार है, उदाहरण के लिए, अन्य विधियों को विरासत में देना अच्छा होगा toString()

थोड़ा और ठोस होने के लिए, मान लें कि हमारे पास निम्नलिखित निर्माण हैं (जाहिर है, यह काम नहीं करता है क्योंकि toString()विरासत में नहीं मिला है, लेकिन क्या यह अच्छा नहीं होगा?)

abstract class ResourceId(open val basePath: BasePath, open val id: Id) {

    // non of the subtypes inherit this... unfortunately...
    override fun toString(): String = "/${basePath.value}/${id.value}"
}
data class UserResourceId(override val id: UserId) : ResourceId(UserBasePath, id)
data class LocationResourceId(override val id: LocationId) : ResourceId(LocationBasePath, id)

मान लिया जाये कि हमारे Userऔर Locationसंस्थाओं को उनके उपयुक्त संसाधन आईडी (वापसी UserResourceIdऔर LocationResourceId, क्रमशः) बुला toString()किसी पर ResourceIdकाफी एक अच्छी छोटी प्रतिनिधित्व कि आम तौर पर सभी उप-प्रकारों के लिए मान्य है में परिणाम सकता है: /users/4587, /locations/23आदि दुर्भाग्य से, क्योंकि उपप्रकार के गैर ओवरराइड करने के लिए विरासत में मिला toString()से विधि सार आधार ResourceId, बुला toString()वास्तव में एक कम सुंदर प्रतिनिधित्व में परिणाम: <UserResourceId(id=UserId(value=4587))>,<LocationResourceId(id=LocationId(value=23))>

उपरोक्त मॉडल करने के अन्य तरीके हैं, लेकिन वे तरीके या तो हमें गैर-डेटा-कक्षाओं का उपयोग करने के लिए मजबूर करते हैं (डेटा कक्षाओं के बहुत सारे लाभों को याद कर रहे हैं), या हम toString()अपने सभी डेटा वर्गों में कार्यान्वयन को दोहरा / दोहराते हैं। (कोई उत्तराधिकार नहीं)।


0

आप एक गैर-डेटा वर्ग से एक डेटा वर्ग प्राप्त कर सकते हैं।

आधार वर्ग

open class BaseEntity (

@ColumnInfo(name = "name") var name: String? = null,
@ColumnInfo(name = "description") var description: String? = null,
// ...
)

बालक वर्ग

@Entity(tableName = "items", indices = [Index(value = ["item_id"])])
data class CustomEntity(

    @PrimaryKey
    @ColumnInfo(name = "id") var id: Long? = null,
    @ColumnInfo(name = "item_id") var itemId: Long = 0,
    @ColumnInfo(name = "item_color") var color: Int? = null

) : BaseEntity()

इसने काम कर दिया।


सिवाय इसके कि अब आप नाम और विवरण गुण सेट नहीं कर सकते हैं, और यदि आप उन्हें कंस्ट्रक्टर में जोड़ते हैं, तो डेटा क्लास को वैल / var की आवश्यकता होती है जो बेस क्लास गुणों को ओवरराइड करेगा।
ब्रिल पप्पिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.