2016-06-13 6 views
28

私はKotlinの列挙型で逆引き参照を行う最良の方法を見つけようとしています。 Effective Javaの私の取り組みの1つは、enum内に静的マップを導入して逆引き参照を処理することでした。簡単な列挙型は、このようなコードに私をリードしてKotlinにかけ、これを移植:逆ルックアップを使用したKotlinの有効な列挙型ですか?

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] 
     } 
    } 
} 

私の質問は、これはこれを行うための最善の方法であるか、またはより良い方法はありますか?同様のパターンに従ったいくつかの列挙型を持っているとどうなりますか? Kotlinに、このコードを列挙型でより再利用可能にする方法がありますか?

答えて

50

まず、fromInt()の引数はIntでなくIntでなければなりません。 nullを使用してTypeを取得しようとすると、nullになることは明らかです。呼び出し側もその処理を試みるべきではありません。マップは変更可能な理由もありません。コードはコードは率直に言って、私はそれが再利用可能な解決策を見つけようとする価値があるか分からない、というとても短いこと

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

に減少させることができます。

+0

私はほぼ同じことをお勧めしました。また、 'fromInt'は' Enum.valueOf(String) 'のようにnull以外の値を返します。' 'map [type]?:throw IllegalArgumentException()' ' – mfulton26

+2

null安全性のためのkotlinのサポートが与えられ、 Javaの場合と同じように私を煩わせることはありません。呼び出し元は、null戻り値を処理し、何をすべきかを決定する(コンパイルするか、何かを実行する)ようにコンパイラによって強制されます。 –

+0

良い点。ありがとう。 – mfulton26

5

私は自分自身が手作業でコード化された値のカップルで逆引きをしているのを見つけ、次のようなアプローチをしました。

enum

sが共有インターフェイスを実装します:このインタフェース

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) 
+2

そのような単純な操作のための多くの作業です。 –

+1

シンプルな使用には完全に同意するのは間違いなく良い方法です。与えられた列挙されたメンバの明示的な名前を扱うために、私はすでに上記のコードを持っていました。 – miensol

+0

私はこの解決策を思いつくのに驚いています。 これはスタンダードライブラリにあるべきだと思います。グアバやApacheコモンのようなものがありますが、kotlinのものです。 – humazed

10

JBNizedのソリューション@のために:一般的に

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 

それは仲間についての事は、彼らが(Javaクラスの静的メンバとは違って)

を再利用できるというオブジェクトです
-2

ヴァルT = Type.values()[序]

:)

2

我々は、そのような要素が見つからなかった場合、所与の述部に一致する最初の要素を返し、またはnullたfindを使用することができます。

companion object { 
    fun valueOf(value: Int): Type? = Type.values().find { it.value == value } 
} 
関連する問題