SonarQube 警告:可能会抛出“NullPointerException”; “getBody()”可以返回 null

问题描述 投票:0回答:1
ResponseEntity<CommonResponseDTO> commonResponseDto = testClient.getCategoryById(sampleEntity.getCategoryId());   

if(Objects.nonNull(commonResponseDto) && Objects.nonNull(commonResponseDto.getBody()) && Objects.nonNull(commonResponseDto.getBody().getName())){

SuperDataDto superDataCategoryDto =  SuperDataDto.builder().id(sampleEntity.getCategoryId()).name(commonResponseDto.getBody().getName()).build();

在上面的代码中,SonarQube 抱怨说

A "NullPointerException" could be thrown; "getBody()" can return null. on 

.name(commonResponseDto.getBody().getName())

我什至正在检查

nonNull
是否有
commonResponseDto.getBody().getName())
。我是不是做错了什么?

java sonarqube
1个回答
1
投票

SonarQube 是对的。

if(Objects.nonNull(commonResponseDto)
  && Objects.nonNull(commonResponseDto.getBody())
  && Objects.nonNull(commonResponseDto.getBody().getName())) {
SuperDataDto superDataCategoryDto =  SuperDataDto.builder().id(sampleEntity.getCategoryId()).name(commonResponseDto.getBody().getName()).build();

不保证连续调用的

getBody()
的返回值。这是一个很容易使您的条件抛出的实现:

private int calls = 0;

public Body getBody() {
  ++calls;
  if (calls % 2 == 0) return null;
  return new Body(...);
}

第一次检查是非空的,第二次调用它将返回空并且你的代码会抛出异常。不要依赖方法从多个调用返回相同的引用,如果您需要单个值,请明确它并将其存储在局部变量中:

if (Objects.nonNull(commonResponseDto)) {
  final CommonResponseDTO body = commonResponseDto.getBody();
  if (Objects.nonNull(body)) {
    final String name = body.getName();
    if (Objects.nonNull(name)) {
      SuperDataDto superDataCategoryDto = SuperDataDto.builder()
          .id(sampleEntity.getCategoryId())
          .name(name)
          .build();

请注意,

Objects#nonNull
主要用作 lambda 表达式中的方法引用,如果您简单地编写
obj != null
,您的代码可能会更清晰。

但这很快就会变得丑陋。您可以通过将代码移至一个小的辅助函数来提高代码的清晰度:

private static String nameOfResponse(
    final ResponseEntity<CommonResponseDTO> response) {
  if (response == null) return null;
  final CommonResponseDTO body = commonResponseDto.getBody(); 
  if (body == null) return null;
  return body.getName();
}

// ...
final String name = nameOfResponse(commonResponseDto);
if (name != null) {
  final SuperDataDto superDataCategoryDto = SuperDataDto.builder()
      .id(sampleEntity.getCategoryId())
      .name(name)
      .build();
}

有一点开销的替代方案是将对象包装在

Optional
中,然后转换此可选值:

Optional.ofNullable(commonResponseDto)
    .map(CommonResponseDTO::getBody)
    .map(Body::getName())
    .ifPresent(name -> {
      SuperDataDto superDataCategoryDto = SuperDataDto.builder()
          .id(sampleEntity.getCategoryId())
          .name(name)
          .build();
    });
© www.soinside.com 2019 - 2024. All rights reserved.