Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
嵌套安全调用空检查Kotlin_Kotlin - Fatal编程技术网

嵌套安全调用空检查Kotlin

嵌套安全调用空检查Kotlin,kotlin,Kotlin,我需要在ImageView中显示图像,因此首先需要确保图像url不为null。这3个选项有效吗 回答数据类 data class Answer( val id: Long?, val title: String?, val answerImage: AnswerImage? ) { data class AnswerImage( val x0: AnswerImageData?, val x1: AnswerImageData?,

我需要在ImageView中显示图像,因此首先需要确保图像url不为null。这3个选项有效吗

回答数据类

data class Answer(
    val id: Long?,
    val title: String?,
    val answerImage: AnswerImage?
) {
    data class AnswerImage(
        val x0: AnswerImageData?,
        val x1: AnswerImageData?,
        val x2: AnswerImageData?
    ) {
        data class AnswerImageData(
            val id: String?,
            val url: String?
        )
    }
}
选项1

answer.answerImage?.let { answerImage ->
     answerImage.x0?.let { answerImageData ->
        answerImageData.url?.let {
             //display image
         }
      }
}
选项2

if (answer.answerImage?.x0?.url != null)
{
   //display image
}
选项3

answer.answerImage?.x0?.url?.let {
      //display image
}
简短回答:是的

  • 选项1:如果您确实需要使用
    answerImage
    answerImageData
    执行更多操作,而不仅仅是安全地将其投射,那么这将是一个不错的选择。在这种特定情况下,我们不需要显式声明这些变量。总结:在这种情况下,选项1不是一个很好的解决方案,但它确实有效

  • 选项2:应该有效,因为所有属性都是不可变的。然后,编译器可以在下一行(在
    if
    范围内)推断
    url
    属性仍然是非
    null

  • 选项3:我认为这是最好的一个:作为代码的读取器,这是最容易处理的一个,因为您通常会用这样的代码来完成它:
    。让{safeUrl->..}


根据@Ken Van Hoeylandt的规定,所有3个选项均有效,另一个有效选项可能是使用elvis运算符:

fun尝试显示图像(答案:答案){
val answerImage=answer.answerImage?:返回
val answerImageData=answerImage.x0?:返回
val answerImageDataUrl=answerImageData.url?:返回
//显示图像
}
有一篇关于这个主题的有趣文章

Ken给出了答案(他们都很好,做了同样的事情,最后一篇是如何设计语言来真正、漂亮、整洁地使用!),但我想谈谈实际的数据模型

首先,您说需要检查
AnswerImageData
url
是否为空。但是它可以为null的唯一原因是,您使用
String?
type显式地将它设置为null。带有空
url
AnswerImageData
是否有效?还是它总是需要有一个?我猜是的,而且我猜它也总是需要一个
id
,所以只需将它们设置为非null

data class AnswerImageData(
    val id: String,
    val url: String
)
现在,您的所有
AnswerImageData
对象都保证具有非空值——从这个意义上说,它们都是有效的,这已经融入到您的设计中。所以你不再需要对它们进行空检查了

你的其他类也是一样——你能有一个带有空值的
AnswerImage
?这可能是一个更棘手的问题,让我们假设在
AnswerImage
中需要始终至少有一个AnswerImageData-在这种情况下,可以将第一个设置为非空,其他设置为可选:

data class AnswerImage(
    val x0: AnswerImageData,
    val x1: AnswerImageData?,
    val x2: AnswerImageData?
)
这不一定是最好的方法-我个人更喜欢
vararg
参数,或者某种类型的集合,这样您就可以拥有任意数量的
AnswerImageData
s,并执行
.first(predicate)
之类的操作来轻松地循环它们。但是如果你想要三个插槽,三个参数就是一种方法

Answer
-我猜这需要一个
id
title
answerImage
-如果是这样,不要让它们为空。通过您的类型强制执行有效的结构,这将使您的生活更加轻松!避免可为空的类型也是如此,除非您确实需要它们



我不知道这是否适用于你正在做的事情,但它可能适用,所以值得一提。(如果你想研究的话,这类东西叫做*域驱动设计-基本上通过设计代码、类型、对象等的方式强制执行业务逻辑的规则和结构)

以上所有答案都很好,但我想提一点。您将属性声明为可空,所以我猜您是从其他地方(如果您熟悉clean体系结构,那么可以从数据层)获取它们的

我的建议是为类创建一个域模型,并将数据映射到域模型(具有非空属性)。这样可以在映射器中处理空值。它更干净,遵循关注点分离和单一责任原则

interface Mapper<F, S> {
    fun firstToSecond(first: F): S
    fun secondToFirst(second: S): F
}
类DataToDomainMapper:Mapper{
覆盖乐趣第一到第二(第一:数据模型):DomainModel{
返回域模型(
id=first.id?:-1,
title=first.title?:“无标题”,
answerImage=first.answerImage?:answerImage()
)
}
覆盖乐趣secondToFirst(第二个:DomainModel):数据模型{
返回数据模型(
id=second.id,
title=second.title,
answerImage=second.answerImage
)
}
}

这样,您就不必在代码中的任何其他地方处理null。对于数据验证,您可以检查id是否为负值。我缩短了你的模型,但你明白了

他们编译吗?他们是否正确地检查空值?对那么它们是有效的。这是你可以自己轻松测试的东西。谢谢。然后我将使用选项3。我将AnswerImageData的url设置为null,因为这是我从一个Rest API获得的数据,我不拥有该API,并且没有太多关于其实现的信息,所以我假设它可以为null。这是一个示例请求,假设您返回了一个空url,那么这仍然是您想要保留的有效的
AnswerImageData
(或其他)吗?或者它是无效的,你不打算使用的东西?您可以使用一个函数来获取API响应,验证它,并创建一个
AnswerImageData
if(且仅当!)它具有您需要的所有数据。这样,您只需要创建有效的对象,因此,如果您理解的话,该类不需要能够表示无效状态。所以现在你再也不用担心如何处理它了。我不知道你到底在做什么,也不知道你是否需要保留它们,但这可能会有所帮助!这个我
data class DataModel(
    val id: Long?,
    val title: String?,
    val answerImage: AnswerImage?
)

data class DomainModel(
    val id: Long,
    val title: String,
    val answerImage: AnswerImage
)
class DataToDomainMapper: Mapper<DataModel, DomainModel> {
    override fun firstToSecond(first: DataModel): DomainModel {
        return DomainModel(
            id = first.id ?: -1,
            title = first.title ?: "no title",
            answerImage = first.answerImage ?: AnswerImage()
        )
    }

    override fun secondToFirst(second: DomainModel): DataModel {
        return DataModel(
            id = second.id,
            title = second.title,
            answerImage = second.answerImage
        )
    }
}