如何在Kotlin中实现这个?

huangapple go评论53阅读模式
英文:

How to do this in Kotlin?

问题

我正在尝试创建一个简单的国家数据类。

data class Country(val name: String, val capital: String)

这个代码工作得很好。现在我想添加邻国信息。

data class Country(val name: String, val capital: String, val neighbors: List<Country>)

这会引起问题。加拿大和美国是邻国。如何干净地实例化每个国家对象?我希望不要将邻国元素设为可变的。这可行吗?
(我尝试首先创建没有邻国的国家,然后使用复制构造函数创建带有邻国的国家。但这样一来,equals方法会出现问题。)
这似乎是一个简单的问题,但我不确定如何解决。

英文:

I am trying to create a simple data class for a country.

data class Country(val name: String, val capital: String)

This works fine. Now I want to add neighbors.

data class Country(val name: String, val capital: String, val neighbors: List&lt;Country&gt;)

This causes problems. Canada and USA are neighbors. How can I cleanly instantiate each of these country objects? I would prefer to not make the neighbors element mutable. Is it possible to do this?
(I tried to create countries without neighbors at first and then create ones with neighbors using the copy constructor. But then equals method has issues.)
It seems like a simple issue but I am not sure how to solve.

答案1

得分: 5

我认为你应该用它们的ID来表示邻国,然后创建一个执行查询的函数。以下是我的解决方案:

data class Country(
    val id: Int,
    val name: String,
    val capital: String,
    val neighbors: List<Int> = emptyList(),
) {
    // Function to retrieve neighbors
    fun getNeighbors(countries: List<Country>): List<Country> {
        return countries.filter { it.id in neighbors }
    }
}

val Togo = Country(id = 1, name = "多哥", capital = "洛美", neighbors = listOf(2, 3, 4))
val Benin = Country(id = 2, name = "贝宁", capital = "波多诺伏", neighbors = listOf(1))
val Ghana = Country(id = 3, name = "加纳", capital = "阿克拉", neighbors = listOf(1, 2))
val Burkina = Country(id = 4, name = "布基纳法索", capital = "瓦加杜古", neighbors = listOf(1, 2, 3))

val allCountries = listOf(Togo, Benin, Ghana, Burkina)

// 这里是邻国列表
val beninNeighbors = Benin.getNeighbors(allCountries)

希望这对你有所帮助。

英文:

I think you should rather represent the neighbors by their id and then create a function to perform the query. Here is my solution

data class Country(
    val id : Int,
    val name: String,
    val capital: String,
    val neighbors: List&lt;Int&gt; = emptyList(),
){
    // Function to retrieve neighbors
    fun  getNeighbors(countries : List&lt;Country&gt;) : List&lt;Country&gt;{
        return  countries.filter { it.id in neighbors }
    }
}

val Togo = Country(id = 1, name = &quot;TOGO&quot;, capital = &quot;Lome&quot;, neighbors = listOf(2, 3, 4))
val Benin = Country(id = 2, name = &quot;Benin&quot;, capital = &quot;Porto Novo&quot;, neighbors = listOf(1))
val Ghana = Country(id = 3, name = &quot;Ghana&quot;, capital = &quot;Accra&quot;, neighbors = listOf(1, 2))
val Burkina = Country(id = 4, name = &quot;Burkina Faso&quot;, capital = &quot;Ouagadougou&quot;, neighbors = listOf(1, 2, 3))

val allCountries = listOf(Togo, Benin, Ghana, Burkina)

//Here you have the list of neighbors
val beninNeighbors = Benin.getNeighbors(allCountries)

答案2

得分: 2

不确定您的情况是什么。

但首先,在我的看法中,这个数据表示方式存在问题。
想象一下,例如toString方法。美国将调用邻居的toString方法,它将调用加拿大的toString方法,然后又回到美国,这永远不会结束...

另一种方法是将邻居的逻辑保存在单独的类中。
它可以封装起来,不可能配置不正确的状态,其中美国是加拿大的邻居,但反之则不成立。

它可以看起来像这样:

data class Country(val name: String, val capital: String)

class Neighbours(vararg neighbour: Pair<Country, Country>) {
    private val neighbours: MutableMap<Country, MutableSet<Country>> = mutableMapOf()

    init {
        neighbour.forEach {
            neighbours.getOrPut(it.first) { mutableSetOf() }.add(it.second)
            neighbours.getOrPut(it.second) { mutableSetOf() }.add(it.first)
        }
    }

    fun getNeighbours(c: Country): Set<Country> = neighbours[c] ?: emptySet()
}

以及使用示例

class Model() {
    val canada = Country("Canada", "Ottawa")
    val usa = Country("USA", "Washington")
    val mexico = Country("Mexico", "Mexico City")

    val neighbours = Neighbours(Pair(canada, usa), Pair(usa, mexico))

    fun usage() {
        println(usa.getNeighbours())
    }

    fun Country.getNeighbours() = neighbours.getNeighbours(this)
}

fun main() = Model().usage()
英文:

Not sure what exactly is your case.

But first, in my mind is that this data representation is problematic.
Imagine for example toString method. USA will call toString of neighbors it will call toString on Canada and then back to the USA and this never ends ...

Another approach would be to hold the logic of neighbors in separate class.
It can be encapsulated and it will not be possible to configure an incorrect state, where the USA is a neighbor of Canada but not vice versa.

It can look like this:

data class Country(val name: String, val capital: String)

class Neighbours(vararg neighbour: Pair&lt;Country, Country&gt;) {
    private val neighbours: MutableMap&lt;Country, MutableSet&lt;Country&gt;&gt; = mutableMapOf()

    init {
        neighbour.forEach {
            neighbours.getOrPut(it.first) { mutableSetOf() }.add(it.second)
            neighbours.getOrPut(it.second) { mutableSetOf() }.add(it.first)
        }
    }

    fun getNeighbours(c: Country): Set&lt;Country&gt; = neighbours[c] ?: emptySet()
}

And usage sample

class Model() {
    val canada = Country(&quot;Canada&quot;, &quot;Ottawa&quot;)
    val usa = Country(&quot;USA&quot;, &quot;Washington&quot;)
    val mexico = Country(&quot;Mexico&quot;, &quot;Mexico City&quot;)

    val neighbours = Neighbours(Pair(canada, usa), Pair(usa, mexico))

    fun usage() {
        println(usa.getNeighbours())
    }

    fun Country.getNeighbours() = neighbours.getNeighbours(this)
}

fun main() = Model().usage()

答案3

得分: 1

Edouardsepopo Zinnoussou的答案要求调用者传递allCountries集合。如果您不喜欢这样做,那么这是一个避免这种情况的解决方案:

data class Country(val name: String, val capital: String) {
    val neighbors: Set<Country> by lazy {
        CountryFactory.neighborsByCountry[this]!!
    }
}

...使用lazy来处理数据的延迟绑定。请注意,现在Country对象不会在toString/equality等中公开邻居,但这里的策略是使用工厂来创建国家:

object CountryFactory {
    val togo = Country(name = "TOGO", capital = "Lome")
    val benin = Country(name = "Benin", capital = "Porto Novo")
    val ghana = Country(name = "Ghana", capital = "Accra")
    val burkina = Country(name = "Burkina Faso", capital = "Ouagadougou")
    val neighborsByCountry: Map<Country, Set<Country>> = mapOf(
        togo to setOf(benin, ghana, burkina),
        benin to setOf(togo),
        ghana to setOf(togo, benin),
        burkina to setOf(togo, benin, ghana),
    )
}

@Test
fun togoHasCorrectNeighbors() {
    CountryFactory.togo.neighbors.shouldContainAll(
        CountryFactory.benin, CountryFactory.ghana, CountryFactory.burkina,
    )
}
英文:

Edouardsepopo Zinnoussou's answer requires the caller to pass around the allCountries collection. If you don't like that then here is a solution that avoids that:

data class Country(val name: String, val capital: String) {
    val neighbors: Set&lt;Country&gt; by lazy {
        CountryFactory.neighborsByCountry[this]!!
    }
}

... using lazy to handle the late binding of the data. Note that the Country object now would not expose the neighbors in the toString/equality/etc but the strategy here is to use a Factory to make the countries:

object CountryFactory {
    val togo = Country(name = &quot;TOGO&quot;, capital = &quot;Lome&quot;)
    val benin = Country(name = &quot;Benin&quot;, capital = &quot;Porto Novo&quot;)
    val ghana = Country(name = &quot;Ghana&quot;, capital = &quot;Accra&quot;)
    val burkina = Country(name = &quot;Burkina Faso&quot;, capital = &quot;Ouagadougou&quot;)
    val neighborsByCountry: Map&lt;Country, Set&lt;Country&gt;&gt; = mapOf(
        togo to setOf(benin, ghana, burkina),
        benin to setOf(togo),
        ghana to setOf(togo, benin),
        burkina to setOf(togo, benin, ghana),
    )
}

@Test
fun togoHasCorrectNeighbors() {
    CountryFactory.togo.neighbors.shouldContainAll(
        CountryFactory.benin, CountryFactory.ghana, CountryFactory.burkina,
    )
}

huangapple
  • 本文由 发表于 2023年6月8日 07:35:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76427705.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定