09. 《Lombok 实战 —— @Cleanup & @NonNull》

x33g5p2x  于2021-12-25 转载在 其他  
字(5.0k)|赞(0)|评价(0)|浏览(594)

1. @Cleanup

1.1 @Cleanup 入门使用

你可以使用@Cleanup确保在代码执行后,在退出当前作用域之前自动清除给定资源。你可以通过使用@Cleanup来注释任何局部变量声明,如下所示:

@Cleanup InputStream in = new FileInputStream(“some/file”);

因此,在局部变量作用范围的末尾,调用in.close()。 保证通过try/finally构造运行此调用。 请看下面的示例,看看它是如何工作的。

public class CleanUpTest {
    public static void main(String[] args) throws IOException {
        File file = new File("fileNameScr");
        File file1 = new File("fileNameDest");
        @Cleanup InputStream inputStream = new FileInputStream(file);
        @Cleanup OutputStream outputStream = new FileOutputStream(file1);
        byte[] bytes = new byte[1024];
        int len = -1;
        while (true) {
            len = inputStream.read(bytes);
            if (len == -1) {
                break;
            }
            outputStream.write(bytes,0,len);
        }
    }
}

编译后代码如下:

public class CleanUpTest {
    public static void main(String[] args) throws IOException {
        File file = new File("fileNameScr");
        File file1 = new File("fileNameDest");
        FileInputStream inputStream = new FileInputStream(file);

        try {
            FileOutputStream outputStream = new FileOutputStream(file1);
            try {
                byte[] bytes = new byte[1024];
                boolean var6 = true;
                while(true) {
                    int len = inputStream.read(bytes);
                    if (len == -1) {
                        return;
                    }
                    outputStream.write(bytes, 0, len);
                }
            } finally {
                if (Collections.singletonList(outputStream).get(0) != null) {
                    outputStream.close();
                }
            }
        } finally {
            if (Collections.singletonList(inputStream).get(0) != null) {
                inputStream.close();
            }
        }
    }
}

finally代码块中,发现一个很奇怪的使用方式,我们在一般在进行关闭流时,常常会这样做:

try {
	// 其他操作
}finally {
    if (in != null) {
        in.close();
    }
}

而且很搞笑的是,在lombok的官网给出的示例,也是使用我们这种常用的方式进行关流操作的。而在实际编译后的代码中却并非如此,但是这两种操作各有什么区别呢?为什么lombok要使用Collections.singletonList(inputStream).get(0)种方式呢?

这一点一直没有找到准确的解释。

todo
1.2 @Cleanup 指定清理方法

如果要清理的对象类型没有close()方法,而是其他一些无参数方法,则可以指定此方法的名称,如下所示:

@Cleanup(“dispose”)org.eclipse.swt.widgets.CoolBar bar = new CoolBar(parent,0);

默认情况下,清除方法假定为close()不能通过@Cleanup调用带有1个或多个参数的清理方法

@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
	// 调用的方法的名称,默认是使用 close() 方法。
    // 看到方法名是字符串,就应该知道是反射实现的。
	String value() default "close";
}

@Cleanup 如果清理方法抛出异常,它将吸收掉抛出方法体内的任何异常。这可能导致问题的实际原因被掩埋,因此在选择使用Project Lombok的资源管理时应该考虑这个问题。

1.3 @Cleanup 全局配置

@Cleanup的全局配置基本上没有,仅有一个配置,是否开启@Cleanup的使如下:

lombok.cleanup.flagUsage = [warning | error] (default: not set)

2. @NonNull

or: How I learned to stop worrying and love the NullPointerException.

@NonNull was introduced in lombok v0.11.10.

2.1 @NonNull 入门使用

你可以在方法或构造函数的参数上使用@NonNulllombok为你自动生成null-check语句。

null检查会被添加到方法的最顶层,就像下面代码所示:

if (param == null) {
	throw new NullPointerException("param is marked @NonNull but is null");
}

对于构造函数而言,将在任何显式this()super()调用之后,立即插入空检查。

之前说过@Setter如果对@Setter不了解,可以移步:)方法,并且在@Setter注解中可以使用onParam属性为生成的所有的setter方法的入参添加注解,现在我们刚好结合这两个注解进行测试。先来看看添加后的实体类:

@Getter
@Setter(
        onParam_= {@NonNull}
)
@ToString
public class User {
    private String username;
    private String password;
}

// 编译后
import lombok.NonNull;		// 导入依赖
public class User {
    private String username;
    private String password;
    public User() { }
    public String getUsername() { return this.username; }
    public String getPassword() { return this.password; }

    public void setUsername(@NonNull String username) {
        if (username == null) {
            throw new NullPointerException("username is marked @NonNull but is null");
        } else {
            this.username = username;
        }
    }

    public void setPassword(@NonNull String password) {
        if (password == null) {
            throw new NullPointerException("password is marked @NonNull but is null");
        } else {
            this.password = password;
        }
    }

    public String toString() {
        return "User(username=" + this.getUsername() + ", password=" + this.getPassword() + ")";
    }
}

此处我们着重看看两个Setter方法,在Setter方法中对我们的入参均添加了@NonNull注解,并且在方法体内增加了为空的验证:

if (password == null) {
	throw new NullPointerException("password is marked @NonNull but is null");
}

但是追求细节完美的笔者,还是感觉异常信息可以自定义,或许更好。

2.2 @NonNull 全参数配置

如果lombok为你生成所有GetterSetterhashCode等方法以及构造函数(例如@Data),Lombok总是将字段上@NonNull的各种注释视为生成空值检查的信号。 在参数上使用lombok自己的@lombok.NonNull会导致在您自己的方法或构造函数中插入null-check语句,show you the code:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @NonNull
    private String username;
    @NonNull
    private String password;
}

// 编译后:
import lombok.NonNull;
public class User {
    @NonNull
    private String username;
    @NonNull
    private String password;

    @NonNull
    public String getUsername() { return this.username; }
    @NonNull
    public String getPassword() { return this.password; }

    public void setUsername(@NonNull String username) {
        if (username == null) {
            throw new NullPointerException("username is marked @NonNull but is null");
        } else {
            this.username = username;
        }
    }
	// toString hashCode,以及 equals 方法省略。
    public void setPassword(@NonNull String password) {
        if (password == null) {
            throw new NullPointerException("password is marked @NonNull but is null");
        } else {
            this.password = password;
        }
    }

    public User(@NonNull String username, @NonNull String password) {
        if (username == null) {
            throw new NullPointerException("username is marked @NonNull but is null");
        } else if (password == null) {
            throw new NullPointerException("password is marked @NonNull but is null");
        } else {
            this.username = username;
            this.password = password;
        }
    }
}

在编译后的class文件中,所有的Getter都在方法上添加了@NonNull,在所有Setter方法和带参构造方法的所有入参添加@NonNull注解。

2.3 @NonNull 全局配置
# 指定抛出异常类型,当然也不是什么类型的异常都可以使用的(tips:希望后期可以支持自定义异常)
# 默认使用的是 NullPointerException
lombok.nonNull.exceptionType = [NullPointerException | IllegalArgumentException] (default: NullPointerException).
# 是否允许使用 @NonNull 注解
lombok.nonNull.flagUsage = [warning | error] (default: not set)

参考文档

上一篇:08. Lombok @Log

相关文章