首页 > 解决方案 > 在Android中通过字符串名称获取类以避免重复自己

问题描述

所以在我的代码中,我得到了一个字符串列表,它们对应于枚举中的电影类型。然后,我将获取一个元数据文件,并浏览每种类型,查看服务器更新该类型的日期是否比我检索这些电影并将它们存储在数据库中的日期更新。长话短说,元数据文件为每种类型都有类,我正在重复自己,只是因为我想不出如何以编程方式让我的应用程序知道,例如,如果您正在评估类型 ACTION,您应该查看成员变量 metadata.action。这是我的代码,我已将其截断,但有几十种类型。

Tw元数据

data class TwMetadata (

        @SerializedName("action") var action : Action,
        @SerializedName("adventure") var adventure : Adventure,
        @SerializedName("animation") var animation : Animation,
        @SerializedName("comedy") var comedy : Comedy,

)

在下面的代码中,我获取了从服务器中提取的字符串列表,将它们转换为流派枚举,并检查 sharedpreferences 元数据文件 (spObj) 中的最后更新日期与最后更新的日期新提取的元数据文件(metadata.value)。如果有新数据,我将更改 sharedpreferences 元数据文件中的日期,我将在此完成后提交。然后我将它发送到一个函数,该函数从数据库或网络中提取数据,具体取决于我发送它的布尔值。理想情况下,我可以摆脱整个“何时”块,只在每种类型上运行一个函数,但要做到这一点,他们需要知道它们在元数据文件中对应的类。

  private fun checkGenreDate(genreList: MutableList<GENRE>) : Completable {
    return Completable.create { emitter ->

      var getNew = json.isNullOrEmpty()

      genreList.forEach {
        when (it) {
          GENRE.NULL -> {
          }
          GENRE.ACTION -> {
            if (spObj.action.updated < metadata.value!!.action.updated) {
              getNew = true
              spObj.action = metadata.value!!.action
            }
            loadFromTwApi(it, getNew)
          }
          GENRE.ADVENTURE -> {
            if (spObj.adventure.updated < metadata.value!!.adventure.updated) {
              getNew = true
              spObj.adventure = metadata.value!!.adventure
            }
            loadFromTwApi(it, getNew)
          }
          GENRE.ANIMATION -> {
            if (spObj.animation.updated < metadata.value!!.animation.updated) {
              getNew = true
              spObj.animation = metadata.value!!.animation

            }
            loadFromTwApi(it, getNew)
          }
          GENRE.COMEDY -> {
            if (spObj.comedy.updated < metadata.value!!.comedy.updated) {
              getNew = true
              spObj.comedy = metadata.value!!.comedy
            }
            loadFromTwApi(it, getNew)
          }
          GENRE.RANDOM -> {
          }
          GENRE.THEATER -> {
            loadTheaterData()
          }
        }
      }
      emitter.onComplete()
    }
  }

我的枚举类

enum class GENRE {
  NULL,
  ACTION,
  ADVENTURE,
  ANIMATION,
  COMEDY,
  RANDOM,
  THEATER;

  companion object {
    fun getGenreByName(name: String) = try {
      (name.toUpperCase(Locale.getDefault()))
    } catch (e: IllegalArgumentException) {
      null
    }
  }

标签: androidkotlindrydata-class

解决方案


我建议使用具有抽象元数据获取和设置方法的通用密封类,以及密封类中包含的静态查找方法。几乎 Kotlin 将枚举转换为类层次结构的魔法,因此您可以在它们之间共享和强制执行功能。

所以对于动作和冒险来说,这里有一个updateAllGenres更新所有元数据的函数。

fun updateAllGenres( spObj: TwMetadata, metadata: MetadataFile ) {
    Genre.forEach {
        it.maybeUpdateSharedMetadata( spObj, metadata )
    }
}

sealed class Genre < T : GenreMetadata > {
    companion object {
        fun forEach( callback: ( Genre < * > ) -> Unit ) {
            Genre::class.sealedSubclasses.forEach {
                callback( it.objectInstance as Genre < * > )
            }
        }
    }

    fun maybeUpdateSharedMetadata( spObj: TwMetadata, metadata: MetadataFile ): Boolean {
        val genreMetadataInFile = getGenreMetadata( metadata.value!! )
        return getGenreMetadata( spObj ).updated < genreMetadataInFile.updated
            .also { setGenreMetadataInDictionary( spObj, genreMetadataInFile )
        }
    }

    protected abstract fun getGenreMetadata( metadata: TwMetadata ): T
    protected abstract fun setGenreMetadataInDictionary( metadata: TwMetadata, genreMetadata: T )
}

object ActionGenre : Genre < Action >() {
    override fun getGenreMetadata( metadata: TwMetadata ): Action {
        return metadata.action
    }

    override fun setGenreMetadataInDictionary( metadata: TwMetadata, genreMetadata: Action ) {
        metadata.action = genreMetadata
    }
}

object AdventureGenre : Genre < Adventure >() {
    override fun getGenreMetadata( metadata: TwMetadata ): Adventure {
        return metadata.adventure
    }

    override fun setGenreMetadataInDictionary( metadata: TwMetadata, genreMetadata: Adventure ) {
        metadata.adventure = genreMetadata
    }
}

需要进行更多调整才能准确获得您的功能,但我相信您明白了。此外,您可能还想更进一步,看看将密封的子类和保存元数据的类Action等结合起来Adventure,但这需要更多的工作。

作为参考,上面的接口是:

interface Action : GenreMetadata {}
interface Adventure : GenreMetadata {}

interface GenreMetadata {
    val updated: Long
}

interface TwMetadata {
    var action: Action
    var adventure: Adventure
}

interface MetadataFile {
    val value: TwMetadata?
}

推荐阅读