বিপরীত চেহারা সহ কোটলিনে কার্যকর এনামস?


102

আমি কোটলিনের একটি এনামে 'বিপরীত অনুসন্ধান' করার সর্বোত্তম উপায়টি অনুসন্ধান করার চেষ্টা করছি। ইফেক্টিভ জাভা থেকে আমার গ্রহণের একটি ছিল আপনি বিপরীত চেহারা পরিচালনা করতে এনামের অভ্যন্তরে একটি স্থির মানচিত্র প্রবর্তন করেছিলেন। এটিকে কোটলিনে একটি সরল এনাম দিয়ে পোর্ট করা আমাকে কোডের দিকে নিয়ে যায় যা দেখতে এই জাতীয়:

enum class Type(val value: Int) {
    A(1),
    B(2),
    C(3);

    companion object {
        val map: MutableMap<Int, Type> = HashMap()

        init {
            for (i in Type.values()) {
                map[i.value] = i
            } 
        }

        fun fromInt(type: Int?): Type? {
            return map[type]
        }
    }
}

আমার প্রশ্ন হ'ল এটিই কি সেরা উপায়, না এর চেয়ে ভাল উপায় আছে? যদি আমার বেশ কয়েকটি এনাম থাকে যা একই ধরণের অনুসরণ করে? এনটামগুলিতে এই কোডটিকে আরও পুনরায় ব্যবহারযোগ্য করে তোলার জন্য কি কোটলিনে কোনও উপায় আছে?


আপনার এনুমের আইডি সম্পত্তি এবং সনাক্তকারী আইটেমের সাথে শনাক্তকরণযোগ্য ইন্টারফেস প্রয়োগ করা উচিত বিমূর্ত শ্রেণি গেটেবলবিআইডির প্রসারিত হওয়া উচিত যা আইডিটিইউনম্যালু মানচিত্র ধারণ করে এবং আইডির ভিত্তিতে এনাম মান প্রদান করে। বিশদটি আমার উত্তরে নীচে।
এল্ডার আগালারাভ

উত্তর:


175

প্রথমত, যুক্তি একটি fromInt()হওয়া উচিত Int, একটি নয় Int?। একটি Typeনাল ব্যবহার করার চেষ্টা স্পষ্টতই শূন্যের দিকে পরিচালিত করবে এবং কলার এমনটি করার চেষ্টাও করা উচিত নয়। Mapএছাড়াও চপল করার কোন কারণ নেই। কোডটি হ্রাস করা যেতে পারে:

companion object {
    private val map = Type.values().associateBy(Type::value)
    fun fromInt(type: Int) = map[type]
}

এই কোডটি এতটাই সংক্ষিপ্ত যে, সত্যই, আমি নিশ্চিত নন যে এটি পুনরায় ব্যবহারযোগ্য সমাধানের সন্ধানের জন্য চেষ্টা করা উপযুক্ত।


8
আমি একই প্রস্তাব করতে চলেছিলাম। fromIntEnum.valueOf(String)map[type] ?: throw IllegalArgumentException()
তদতিরিক্ত

4
নাল-সুরক্ষার জন্য কোটলিন সমর্থন দেওয়া, পদ্ধতি থেকে নাল ফিরিয়ে দেওয়া জাভা হিসাবে আমাকে বিরক্ত করবে না: ফোনকারীকে নাল ফেরানো মানটি মোকাবেলা করতে কম্পাইলার দ্বারা বাধ্য করা হবে এবং কী করা উচিত (নিক্ষেপ বা করণীয়) অন্যকিছু).
জেবি নিজত

1
@ রাফেল কারণ এনামগুলি জাভা 5 তে এবং জাভা 8 তে
ptionচ্ছিকভাবে

2
এই কোড ব্যবহার আমার সংস্করণ by lazy{}জন্য mapএবং getOrDefault()দ্বারা ওয়েবকে আরো নিরাপদ অ্যাক্সেসের জন্যvalue
Hoang Tran

2
এই সমাধানটি ভাল কাজ করে। নোট করুন যে Type.fromInt()জাভা কোড থেকে কল করতে সক্ষম হতে আপনার সাথে পদ্ধতিটি বেনিফিট করতে হবে @JvmStatic
আর্টো বেনডিকেন

34

প্রদত্ত প্রাকটিকের সাথে মেলেfind যা প্রথম উপাদানটি ফেরত দেয় আমরা ব্যবহার করতে পারি , বা যদি এরূপ কোনও উপাদান পাওয়া যায় নি তবে শূন্য হয়।

companion object {
   fun valueOf(value: Int): Type? = Type.values().find { it.value == value }
}

4
first { ... }পরিবর্তে একটি সুস্পষ্ট বর্ধন ব্যবহার করা হচ্ছে কারণ একাধিক ফলাফলের জন্য কোনও ব্যবহার নেই।
ক্রিয়েটিভ ক্রিয়েটোরমায়াবেনট

9
না, ব্যবহার firstকরা কোনও উন্নতি নয় কারণ এটি আচরণটি পরিবর্তন করে এবং NoSuchElementExceptionযদি আইটেমটি পাওয়া যায় না findযা firstOrNullফেরতের সমান হয় null। সুতরাং আপনি যদি নাল ব্যবহারের পরিবর্তে নিক্ষেপ করতে চানfirst
হামাজড

এই পদ্ধতিটি একাধিক মান সহ এনামগুলির সাথে ব্যবহার করা যেতে পারে: fun valueFrom(valueA: Int, valueB: String): EnumType? = values().find { it.valueA == valueA && it.valueB == valueB } এছাড়াও মানগুলি এনামে না থাকলে আপনি একটি ব্যতিক্রমও ছুঁড়ে ফেলতে পারেন: fun valueFrom( ... ) = values().find { ... } ?: throw Exception("any message") বা এই পদ্ধতিটি কল করার সময় আপনি এটি ব্যবহার করতে পারেন: var enumValue = EnumType.valueFrom(valueA, valueB) ?: throw Exception( ...)
অষ্টম

আপনার পদ্ধতিতে রৈখিক জটিলতা O (n) রয়েছে। হে (1) জটিলতার সাথে পূর্বনির্ধারিত হ্যাশম্যাপে লুকিং ব্যবহার করা ভাল।
এলদার আগালারভ

হ্যাঁ, আমি জানি তবে বেশিরভাগ ক্ষেত্রে, এনামের খুব কম সংখ্যক রাজ্য থাকবে সুতরাং এটি কোনওভাবেই আসে না, এর চেয়ে বেশি পাঠযোগ্য।
হামেজ হয়েছে

27

এটি এক্ষেত্রে খুব বেশি বোঝায় না, তবে @ জেবিএনাইজডের সমাধানের জন্য এখানে একটি "লজিক নিষ্কাশন" দেওয়া হয়েছে:

open class EnumCompanion<T, V>(private val valueMap: Map<T, V>) {
    fun fromInt(type: T) = valueMap[type]
}

enum class TT(val x: Int) {
    A(10),
    B(20),
    C(30);

    companion object : EnumCompanion<Int, TT>(TT.values().associateBy(TT::x))
}

//sorry I had to rename things for sanity

সাধারণভাবে এটি সাহাবী অবজেক্টগুলির বিষয়ে যা সেগুলি পুনরায় ব্যবহার করা যেতে পারে (জাভা শ্রেণীর স্থির সদস্যদের মতো নয়)


কেন আপনি ওপেন ক্লাস ব্যবহার করেন? কেবল এটি বিমূর্ত করুন।
এল্ডার আগালারভ

21

আরও একটি বিকল্প, যা আরও "আইডেম্যাটিক" হিসাবে বিবেচিত হতে পারে, নিম্নলিখিতটি হবে:

companion object {
    private val map = Type.values().associateBy(Type::value)
    operator fun get(value: Int) = map[value]
}

যা তখন ব্যবহার করা যেতে পারে Type[type]


নিশ্চয়ই আরও বুদ্ধিমান! চিয়ার্স।
আলেকসান্দ্র

6

আমি নিজেকে কাস্টম, হাত কোডড, কয়েক বার মান দিয়ে বিপরীত অনুসন্ধান করতে দেখেছি এবং নিম্নলিখিত পদ্ধতির সাথে এসেছি।

enumএকটি ভাগ করা ইন্টারফেস বাস্তবায়ন করুন :

interface Codified<out T : Serializable> {
    val code: T
}

enum class Alphabet(val value: Int) : Codified<Int> {
    A(1),
    B(2),
    C(3);

    override val code = value
}

এই ইন্টারফেসটি (তবে আশ্চর্যজনক নামটি হল :)) সুস্পষ্ট কোড হিসাবে একটি নির্দিষ্ট মান চিহ্নিত করে। লক্ষ্যটি লিখতে সক্ষম হ'ল:

val a = Alphabet::class.decode(1) //Alphabet.A
val d = Alphabet::class.tryDecode(4) //null

যা নিম্নলিখিত কোড সহ সহজেই অর্জন করা যায়:

interface Codified<out T : Serializable> {
    val code: T

    object Enums {
        private val enumCodesByClass = ConcurrentHashMap<Class<*>, Map<Serializable, Enum<*>>>()

        inline fun <reified T, TCode : Serializable> decode(code: TCode): T where T : Codified<TCode>, T : Enum<*> {
            return decode(T::class.java, code)
        }

        fun <T, TCode : Serializable> decode(enumClass: Class<T>, code: TCode): T where T : Codified<TCode> {
            return tryDecode(enumClass, code) ?: throw IllegalArgumentException("No $enumClass value with code == $code")
        }

        inline fun <reified T, TCode : Serializable> tryDecode(code: TCode): T? where T : Codified<TCode> {
            return tryDecode(T::class.java, code)
        }

        @Suppress("UNCHECKED_CAST")
        fun <T, TCode : Serializable> tryDecode(enumClass: Class<T>, code: TCode): T? where T : Codified<TCode> {
            val valuesForEnumClass = enumCodesByClass.getOrPut(enumClass as Class<Enum<*>>, {
                enumClass.enumConstants.associateBy { (it as T).code }
            })

            return valuesForEnumClass[code] as T?
        }
    }
}

fun <T, TCode> KClass<T>.decode(code: TCode): T
        where T : Codified<TCode>, T : Enum<T>, TCode : Serializable 
        = Codified.Enums.decode(java, code)

fun <T, TCode> KClass<T>.tryDecode(code: TCode): T?
        where T : Codified<TCode>, T : Enum<T>, TCode : Serializable
        = Codified.Enums.tryDecode(java, code)

3
এই জাতীয় অপারেশনের জন্য এটি অনেক কাজ, স্বীকৃত উত্তরটি অনেক ক্লিনার আইএমও
কনার ওয়াই্যাট

2
সাধারণ ব্যবহারের জন্য সম্পূর্ণরূপে সম্মত হন এটি অবশ্যই আরও ভাল। প্রদত্ত গণিত সদস্যের জন্য সুস্পষ্ট নামগুলি হ্যান্ডেল করার জন্য আমার কাছে ইতিমধ্যে উপরের কোডটি ছিল।
মিনসোল

আপনার কোড প্রতিবিম্ব ব্যবহার করে (খারাপ) এবং ফুলে যায় (খুব খারাপ)।
এল্ডার আগালরভ

1

পূর্ববর্তী কিছু প্রস্তাবগুলির একটি বৈকল্পিকটি নিম্নলিখিত ক্ষেত্র এবং অর্ডারিনাল ফিল্ড এবং গেটভ্যালু ব্যবহার করে হতে পারে:

enum class Type {
A, B, C;

companion object {
    private val map = values().associateBy(Type::ordinal)

    fun fromInt(number: Int): Type {
        require(number in 0 until map.size) { "number out of bounds (must be positive or zero & inferior to map.size)." }
        return map.getValue(number)
    }
}

}


1

আর একটি উদাহরণ বাস্তবায়ন। এটি কোনও ডিফল্ট মান (এখানে এখানে OPEN) সেট করে যদি কোনও ইনপুট কোনও এনাম বিকল্পের সাথে মেলে না:

enum class Status(val status: Int) {
OPEN(1),
CLOSED(2);

companion object {
    @JvmStatic
    fun fromInt(status: Int): Status =
        values().find { value -> value.status == status } ?: OPEN
}

}


0

আরও জেনেরিক সমাধান নিয়ে এসেছেন

inline fun <reified T : Enum<*>> findEnumConstantFromProperty(predicate: (T) -> Boolean): T? =
T::class.java.enumConstants?.find(predicate)

ব্যবহারের উদাহরণ:

findEnumConstantFromProperty<Type> { it.value == 1 } // Equals Type.A

0

সত্য আইডিয়োমেটিক কোটলিন ওয়ে। পুষ্পিত প্রতিবিম্ব কোড ব্যতীত:

interface Identifiable<T : Number> {

    val id: T
}

abstract class GettableById<T, R>(values: Array<R>) where T : Number, R : Enum<R>, R : Identifiable<T> {

    private val idToValue: Map<T, R> = values.associateBy { it.id }

    operator fun get(id: T): R = getById(id)

    fun getById(id: T): R = idToValue.getValue(id)
}

enum class DataType(override val id: Short): Identifiable<Short> {

    INT(1), FLOAT(2), STRING(3);

    companion object: GettableById<Short, DataType>(values())
}

fun main() {
    println(DataType.getById(1))
    // or
    println(DataType[2])
}

-1

ভাল টি = টাইপ.ভ্যালু () [অর্ডিনাল]

:)


এটি 0, 1, ..., এন ধ্রুবকের পক্ষে কাজ করে যদি আপনার যদি 100, 50, 35 এর মতো থাকে তবে এটি কোনও সঠিক ফলাফল দেয় না।
কুলমাইন্ড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.