有没有更好的方法来处理深度嵌套数据的空异常?

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

Is there a better way to handle null exceptions for deeply nested data?

问题

我想知道是否有更好的方法来处理深度嵌套数据的空值?假设我有4个类,分别是UniversityBranchStudentInformation,它们相互嵌套。

class University {
    Branch branch;
    // getters & setters
}

class Branch {
    Student student;
    // getters & setters
}

class Student {
    Information information;
    // getters & setters
}

class Information {
    String name;
    List<Boolean> attendance;
    // getters & setters
}

出于某种原因,我希望获取学生的姓名,为此我们只需导航到

university.getBranch().getStudent().getInformation().getName()

这可能会引发空指针异常。因此,我们总是在其周围放置一个if块来检查空值,类似于这样

if (university != null && university.getBranch() != null ...)

以上嵌套深度为4,有时深度可能超过8或9。在这些情况下,像上面那样的做法不太合理,我们更愿意牺牲清晰的代码。我需要一个更好的方法来解决这个问题。

以下是我发现有帮助的一些方法,它们各自有自己的限制:

  1. PropertySource.getNestedProperty() - Apache common beanUtils库,几乎可以满足我的需求,但它会抛出NestedNullException,需要使用try{}catch(){}来处理,但出于某种原因,我们不想这样做。在处理列表时也存在问题。

  2. Optionals - Java 8提供了最终的Optional类

// 传统方式:
if (university != null) {
    Branch branch = user.getBranch();
    if (branch != null) {
        Student student = branch.getStudent();
        if (student != null) {
            Information info = student.getInformation();
            if (info != null) {
                name = info.getName();
            }
        }
    }
}

// Java 8 Optionals:
Optional<String> optional = Optional.ofNullable(user)
  .flatMap(University::getBranch)
  .flatMap(Branch::getStudent)
  .map(Student::getInformation)
  .orElse("default");

现在代码看起来更干净,但如果我们要多次使用该Optional,就需要为每个Optional创建一个对象。

  1. 第三种方法是使用对象映射器(Object Mapper)将整个对象转换为JSON,然后直接获取数据
responseEntityJson = objectMapper.writeValueAsString(responseEntity);
rootNode = objectMapper.readTree(responseEntityJson);
DataValue = rootNode.findValue("Data");

我期待了解您的意见。

英文:

I wonder whether there is a better way of handling nulls for deeply nested data?
Lets say i have 4 classes namely University, Branch, Student, Information nested one around other.

class University{
Branch branch;
//getters &amp; setters
}
class Branch{
Student student;
//getters &amp; setters
}
class Student{
Information information;
//getters &amp; setters
}
class Information{
String name;
List&lt;Boolean&gt; attendance;
//getters &amp; setters
}

For some reason, i want student Name to be consumed, for which we just navigate
university.getBranch().getStudent().getInformation().getName()
which is prone to raise null exceptions.
For that reason we always put an if block around it to check nulls,Something like this
if(university != null &amp;&amp; university.getBranch() != null ..... )
Above the nested depth is 4, sometimes i have depth more than 8 or 9. In those situations, something like above doesn't make sense, we are rather sacrificing clean code.
I need a better approach for this problem

Below are few ways i found helpful with their own limitations:

  1. PropertySource.getNestedProperty() - Apache common beanUtils library, which almost worked for me except it throws NestedNullException which needs to be handled using try{}catch(){} which for some reason, we don't want to do. Even have issues when dealing with lists.
  2. Optionals - Java 8 provides final Class Optional
*Traditional Way :*
if (university != null) {
    Branch branch = user.getBranch();
    if (branch != null) {
        Student student = branch.getStudent();
        if (student != null) {
            Information info = student.getInformation();
            if (info != null) {
                name = info.getName();
            }
        }
    }
}

*Java 8 Optionals :*
Optional&lt;String&gt; optional = Optional.ofNullable(user)
  .flatMap(University::getBranch)
  .flatMap(Branch::getStudent)
  .map(Student::getInformation)
  .orElse(&quot;default&quot;);


Code looks much clean now except we need to create an object for every Optional(if we want to use that optional more than once)

  1. The Third way of doing is JSONfying entire object using Object Mapper and get that data directly
responseEntityJson = objectMapper.writeValueAsString(responseEntity);
		rootNode = objectMapper.readTree(responseEntityJson);
		DataValue = rootNode.findValue(&quot;Data&quot;);

I'm looking forward to know your opinions.

答案1

得分: 2

IMHO,尽可能避免各种对象的空值可能性是最佳选择。

例如,如果您的大学有一个学生列表/集合,getStudents() 不应该返回 null。但可以返回一个空集合。如果 University 类确保 getStudents() 不会返回 null,则可以在调用它的任何地方省略对 getStudents() 的空值检查。

此外,对于许多其他可以通过 getter 方法接收的值,它们在逻辑上不应该为空,所以让对象确保它永远不会返回 null。如果字段是不可变的,您可以在构造函数中检查空值,然后确保您永远不会收到 null

如果某些情况下确实可能不存在,Optional 是一个很好的选择。

英文:

IMHO, it is best to avoid the possibility of null values for as many kinds of objects as possible.

For example, if your university has a list/set of students, getStudents() should never return null. It may return an empty collection, though. If the University class makes sure that getStudents() never return null, you can skip the null checks for getStudents() wherever you call it.

Also, for many other values that can be received by getters, they should logically never be null, so just let the object make sure it never returns null. If the field is immutable, you can check in the constructor for null values and later be sure that you never receive null.

If something can really be absent, Optional is a good alternative.

huangapple
  • 本文由 发表于 2020年8月4日 19:08:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/63245608.html
匿名

发表评论

匿名网友

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

确定