英文:
Correct way to handle circular reference problem in kotlin
问题
以下是您要翻译的代码部分:
我正在尝试构建一个简单的 JSON 解析器,下面是我的 JSON 类型表示,使用了密封类
sealed interface JValue {
data class JString(val value: String): JValue
data class JNumber(val value: Float): JValue
data class JBool(val value: Boolean): JValue
data object JNull: JValue
data class JObject(val value: Map<String, JValue>): JValue
data class JArray(val value: List<JValue>): JValue
}
val jValueArr: JValue = getJArrayValue()
val jValueString: JValue = JValue.JString("Test")
val jValueNumber: JValue = JValue.JNumber(100f)
val jArray = JValue.JArray(listOf(jValueString, jValueNumber, jValueArr))
fun getJArrayValue(): JValue.JArray = JValue.JArray(listOf(jValueArr))
fun main() {
println(jArray.value)
}
我明白问题出在 JObject
和 JArray
,因为它们可以持有一个 JValue
(循环依赖?)
如果我运行上述代码,它会打印以下值作为 jArray
[JString(value=Test), JNumber(value=100.0), JArray(value=[null])]
正如您所看到的,第三个元素是 JArray(value=[null])
而不是 jValueArr
,但我可以理解这是因为 Kotlin 惰性地初始化对象,并且在调用 getJArrayValue()
时 jValueArr
尚未初始化。
我的问题是,我如何正确处理这种情况?
链接到我的实际代码 - https://github.com/VenkateswaranJ/ParserCombinatorsKT/blob/master/src/main/kotlin/JsonParser.kt#L98-L130
更新:
我在解析器中通过一些虚拟的前向引用来修复了它(不确定这是否是最佳方法)
这是代码 - https://github.com/VenkateswaranJ/ParserCombinatorsKT/blob/master/src/main/kotlin/JsonParser.kt#L16-L26
英文:
I'm trying to build a simple json parser and below is my JSON type representation with a sealed class
sealed interface JValue {
data class JString(val value: String): JValue
data class JNumber(val value: Float): JValue
data class JBool(val value: Boolean): JValue
data object JNull: JValue
data class JObject(val value: Map<String, JValue>): JValue
data class JArray(val value: List<JValue>): JValue
}
val jValueArr: JValue = getJArrayValue()
val jValueString: JValue = JValue.JString("Test")
val jValueNumber: JValue = JValue.JNumber(100f)
val jArray = JValue.JArray(listOf(jValueString, jValueNumber, jValueArr))
fun getJArrayValue(): JValue.JArray = JValue.JArray(listOf(jValueArr))
fun main() {
println(jArray.value)
}
The problematic parts are JObject
and JArray
as they can hold a JValue
(circular dependency?)
If I run the above code it prints the below value as jArray
[JString(value=Test), JNumber(value=100.0), JArray(value=[null])]
as you see the third element is JArray(value=[null])
instead of jValueArr
, but I can understand that it's null because the kotlin initializes the objects lazy and jValueArr
won't be initialized while calling getJArrayValue()
.
My question is, how can I properly handle this scenario?
Link to my actual code - https://github.com/VenkateswaranJ/ParserCombinatorsKT/blob/master/src/main/kotlin/JsonParser.kt#L98-L130
Update:
I fixed it in my parser with some dummy forward reference (not sure it's the best way of doing it)
Here is the code - https://github.com/VenkateswaranJ/ParserCombinatorsKT/blob/master/src/main/kotlin/JsonParser.kt#L16-L26
答案1
得分: 1
在大多数情况下,不可能存在双向都是只读的循环引用。我们必须至少使一侧可变。
在您的特定情况下,我们有一个列表,因此可以最初创建一个可变列表,然后将其设置为只读:
val jValueArr: JValue = run {
val list = mutableListOf<JValue>()
JValue.JArray(list).also { list += it }
}
上述解决方案可能被认为不是完全安全的,因为我们可以将列表转回MutableList
并对其进行修改。或者,我们可以创建自己的MutableList
实现,不允许在将其切换为只读后进行修改。或者,我们可以使用 buildList() ,它已经提供了这样的实现。
请注意,您不能打印具有循环引用的数据类,因为这样会导致无限递归。此外,在与JSON相关的数据结构中使用循环引用似乎很奇怪。JSON 不支持循环引用。
英文:
In most cases it is not possible to have a circular reference where both sides are read-only. We have to make at least one side mutable.
In your specific case we have a list, so we can initially create a mutable list and then make it read-only:
val jValueArr: JValue = run {
val list = mutableListOf<JValue>()
JValue.JArray(list).also { list += it }
}
Above solution may be considered not exactly safe, as we can cast the list back to MutableList
and mutate it. Alternatively, we can create our own implementation of MutableList
which doesn't allow mutating after switching it to read-only. Or we can use buildList() which provides such implementation already.
Please be aware you can't print data classes with circular references, because then you get into an infinite recursion. Also, it feels strange to use circular references in a data structure which seems related to JSON. JSON doesn't support circular references.
答案2
得分: 0
你可以通过延迟初始化jValueArr来解决循环初始化问题:
val jValueArr by lazy { getJArrayValue() }
除非你声明变量为懒加载,否则它会按声明顺序进行初始化。
然而,在你当前的设计中,仍然存在循环依赖问题。你的getJArrayValue函数会创建一个包含jValueArr本身的列表的JArray。这可能导致无限递归,因为它本质上是一个循环结构,jValueArr包含它本身,而它包含它本身,依此类推。
你应该重新设计你的数据结构以避免无限递归。
英文:
You can fix the circular initialization problem with lazy initialization of jValueArr:
val jValueArr by lazy { getJArrayValue() }
unless you declare your variable lazy, it is initialized in the order of declaration.
Yet, you will still have a circular dependency issue, in your current design, your getJArrayValue function is creating a JArray with a list that contains jValueArr itself. This can lead to infinite recursion, as it's essentially a circular structure where jValueArr contains itself, which contains itself, and so on.
You should redesign your data to avoid endless recursion.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论