【问题标题】:Reference an object in a class by using a string?使用字符串引用类中的对象?
【发布时间】:2022-01-09 16:20:18
【问题描述】:

我想在下面的这个类中引用一个对象:

class HerbData {
    object Dill {
        const val herbName: String = "This is Dill!"
        const val scientificName: String = "Anethum Graveolens"
        val dullThumbnail: Int = R.drawable.dill_thumbnail_attr
    }

    object Peppermint {
        val herbName: String = "This is Peppermint!"
    }
}

无论如何我可以通过在 Kotlin 中使用字符串来引用该对象吗?这有点我的意思:

HerbData."Dill".herbname

我找不到任何关于 Kotlin 的主题。

【问题讨论】:

  • 不,您不应该以这种方式使用字符串。相反,您可能需要一个适当的数据结构,例如 Map 来存储值。

标签: android string class kotlin


【解决方案1】:

另一种方法是使用枚举类。相对于地图的优势在于您拥有可以直接在代码中引用的数据结构,因此您可以使用HerbData.DillHerbData["Dill"]。这将使您能够利用编译时检查和 lint 警告、重构、详尽的模式匹配、代码完成等,因为数据是在您的代码中定义的

enum class HerbData(
    val herbName: String,
    val scientificName: String? = null,
    val dullThumbnail: Int? = null
) {
    Dill("This is Dill!", "Anethum Graveolens", R.drawable.dill_thumbnail_attr),
    Peppermint("This is Peppermint!");
    
    companion object {
        operator fun get(name: String): HerbData? =
            try { valueOf(name) } catch(e: IllegalArgumentException) { null }
    }
}

fun main() {
    // no guarantee these lookups exist, need to null-check them
    HerbData["Peppermint"]?.herbName.run(::println)
    // case-sensitive so this fails
    HerbData["peppermint"]?.herbName.run(::println)

    // this name is defined in the type system though! No checking required
    HerbData.Peppermint.herbName.run(::println)
}

>> This is Peppermint!
null
This is Peppermint!

Enum 类有 valueOf(String) 方法,可以让您按名称查找常量,但如果没有匹配项,它会抛出异常。我将它添加为类中的get 运算符函数,因此您可以像地图一样使用典型的getter 访问(例如HerbData["Dill"])。作为替代方案,您可以做一些更整洁的事情:

companion object {
    // storing all the enum constants for lookups
    private val values = values()
    operator fun get(name: String): HerbData? =
        values.find() { it.name.equals(name, ignoreCase = true) }
}

您可以调整效率(我只是存储 values() 的结果,因为该调用每次都会创建一个新数组)但这很简单 - 您只是存储所有枚举条目并创建一个查找根据名称。如果需要,这可以让您变得更聪明一些,例如使查找不区分大小写(这可能是也可能不是一件好事,具体取决于您这样做的原因)

这里的优点是您自动生成查找 - 如果您重构枚举常量的名称,字符串标签将始终匹配它(您可以使用枚举常量本身的 name 属性获取)。当然,代码中的任何“Dill”字符串都将保留为“Dill”——这是使用硬编码字符串查找的限制


真正的问题是,您为什么要这样做?如果它是纯数据,不需要在代码中显式引用任何项目,并且它们都在运行时查找,那么您可能应该使用数据类和映射,或者类似的东西。如果您确实需要在编译时将它们作为代码中的对象引用(并且尝试使用 HerbData."Dill".herbName 意味着您这样做),那么枚举是一种相当简单的方法让您同时做到这一点

【讨论】:

    【解决方案2】:

    声明一个数据类

    data class HerbData (
    val scientificName: String,
    val dullThumbnail: Int
    )
    

    初始化一个可变映射并将数据放入其中

    val herbData = mutableMapOf<String, HerbData>()
    herbData.put("Dill", HerbData("Anethum Graveolens", R.drawable.dill_thumbnail_attr))
    herbData.put("Peppermint", HerbData("Mentha piperita", R.drawable.peppermint_thumbnail_attr))
    

    你现在可以

    herbData["Dill"]?.scientificName
    

    【讨论】:

      【解决方案3】:
      class HerbData {
      
        interface Herb {
          val herbName: String
          val scientificName: String
        }
      
        object Dill : Herb {
          override val herbName: String = "This is Dill!"
          override val scientificName: String = "Anethum Graveolens"
        }
      
        object Peppermint: Herb {
          override val herbName: String = "This is Peppermint!"
          override val scientificName: String = "Mentha piperita"
        }
      
        companion object {
          operator fun get(name: String): Herb? {
            return HerbData::class
              .nestedClasses
              .find { it.simpleName == name }
              ?.objectInstance as? Herb
          }
        }
      
      }
      
      println(HerbData["Dill"]?.herbName)               // Prints: This is Dill!
      println(HerbData["Peppermint"]?.scientificName)   // Prints: Mentha piperita
      println(HerbData["Pepper"]?.herbName)             // Prints: null
      

      【讨论】:

        猜你喜欢
        • 2013-09-02
        • 1970-01-01
        • 1970-01-01
        • 2013-07-03
        • 1970-01-01
        • 2012-05-22
        • 2018-06-29
        • 1970-01-01
        • 2019-04-10
        相关资源
        最近更新 更多