03. 《Lombok 实战 —— @toString & @EqualsAndHashCode》

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

1. @toString

1.1 入门使用@toString

任何类定义都可以用@ToString注释,让lombok生成toString()方法的实现。 默认情况下,它会按顺序打印类名和每个字段,并以逗号分隔,如下所示:

@Getter
@Setter
@ToString
public class User {
    private String username;
    private String password;
    private boolean isAdult;
}

// 编译后:
public class User {
    private String username;
    private String password;
    private boolean isAdult;
	// 其他省略
    public String toString() {
        return "User(username=" + this.getUsername() + 
        ", password=" + this.getPassword() + 
        ", isAdult=" + this.isAdult() + ")";
    }
}

在编译后地结果中可以看到,lombok默认使用的是getter方法进行设值,如this.getUsername(),但是我们实际并不需要通过getter的方式进行设置,lombok也知道我们有这样的需求,为我们提供了注解配置项,下面我们就来继续看看如何进行更个性化的配置@toString

1.2 深度研究@toString

深度研究一个东西,肯定少不了源码的分析,当然@toString的代码并不难,放一百个心,如下所示:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
	// 输出是否带字段名称
	boolean includeFieldNames() default true;
	// 排除字段,以 String[] 格式设置
	String[] exclude() default {};
	// 指定输出哪些字段,不过将来会被标注@deprecated,使用 @ToString.Include 替代
	String[] of() default {};
	// 是否调用父类的 toString() 方法
	boolean callSuper() default false;
	// 不使用 getter 方法获取字段值
	boolean doNotUseGetters() default false;
	// 是否只输出被 @ToString.Include 备注的属性,默认情况输出所有非静态字段
	boolean onlyExplicitlyIncluded() default false;
	
	// 标注不被输出的字段
	@Target(ElementType.FIELD)
	@Retention(RetentionPolicy.SOURCE)
	public @interface Exclude {}
	
	// 标注被输出的字段 @ToString.Include
	@Target({ElementType.FIELD, ElementType.METHOD})
	@Retention(RetentionPolicy.SOURCE)
	public @interface Include {
		// 输出字段的顺序,值越高越先被输出
		int rank() default 0;
		// 默认为带注释的成员的字段/方法名称。可以使用此方式使用新的名称
		String name() default "";
	}
}

下面我们快速写点demo测试一遍,以下的代码实例只针对toString部分

  • 是否显示字段名
@ToString(
        includeFieldNames = false
)
@Getter @Setter
public class User {
    private String username,password;
    private boolean isAdult;
}

// 编译后:
public String toString() {
    return "User(" + this.getUsername() + ", " + this.getPassword() + ", " + this.isAdult() + ")";
}
  • 是否使用getter获取值
@ToString(
        doNotUseGetters = true
)
@Getter @Setter
public class User {
    private String username,password;
    private boolean isAdult;
}
// 编译后:
public String toString() {
	return "User(username=" + this.username + ", password=" + this.password + ", isAdult=" + this.isAdult + ")";
}
  • 排除部分字段不输出
@ToString(
        doNotUseGetters = true,
        exclude = {"password","isAdult"}
)
@Getter @Setter
public class User {
    private String username,password;
    private boolean isAdult;
}

// 编译后:
public String toString() {
	return "User(username=" + this.username + ")";
}
  • 只输出指定字段
@ToString(
        doNotUseGetters = true,
        onlyExplicitlyIncluded = true
)
@Getter @Setter
public class User {
    @ToString.Include		// 指定字段
    private String username,password;
    private boolean isAdult;
}

// 编译后:
public String toString() {
	return "User(username=" + this.username + 
		", password=" + this.password + ")";
}
  • 对指定输出字段进行排序,以及设置别名
@ToString(
        doNotUseGetters = true,
        onlyExplicitlyIncluded = true
)
@Getter @Setter
public class User {
    @ToString.Include(rank = 1)
    private String username;
    @ToString.Include(rank = 2,name = "pwd")
    private String password;
    private boolean isAdult;
}

// 编译后:
public String toString() {
	return "User(pwd=" + this.password + ", username=" + this.username + ")";
}

在上面,我故意设置password别名为pwd,并设置输出rank值大于usernamerank的值。

1.3 @toString全局配置
# 是否指定只有标注了 @ToString.Include 注解的属性字段才被输出
lombok.toString.includeFieldNames = [true | false] (default: true)
# 是否指定使用 getter 方法获取字段属性值,默认是使用 getter
lombok.toString.doNotUseGetters = [true | false] (default: false)
# 是否调用父类 toString 方法。
# call:调用父类toString方法
# skip:不调用父类toString方法(建议使用默认值)
# warn:不调用父类toString方法,但是会有提示
lombok.toString.callSuper = [call | skip | warn] (default: skip)
# 如果启用,会将标注了@toString 的注解,提示error或warning
lombok.toString.flagUsage = [warning | error] (default: not set)

如果有疑问请留言!!!

2. @EqualsAndHashCode

Equality made easy: Generates hashCode and equalsimplementations from the fields of your object.

2.1 入门体验 @EqualsAndHashCode

任何类定义都可以使用@EqualsAndHashCode进行注释,lombok会生成equals(Object other)hashCode()方法的实现。

默认情况下,它将使用所有non-staticnon-transient字段,当然你可以通过使用@EqualsAndHashCode.Include标记类成员来修改使用哪些字段,或者使用@ EqualsAndHashCode.Exclude标记类成员不使用哪些字段。

你可以通过使用@EqualsAndHashCode.Include并使用@EqualsAndHashCode(onlyExplicitlyIncluded = true)标记它们来准确指定希望使用的字段或方法。

@ToString
@Getter @Setter
@EqualsAndHashCode
public class User {
    private String username,password;
    private boolean isAdult;
}

// 编译后
public class User {
	// 其他省略
    public boolean equals(Object o) { // 具体实现省略(代码量太多) }
    protected boolean canEqual(Object other) { return other instanceof User; }
    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $username = this.getUsername();
        int result = result * 59 + ($username == null ? 43 : $username.hashCode());
        Object $password = this.getPassword();
        result = result * 59 + ($password == null ? 43 : $password.hashCode());
        result = result * 59 + (this.isAdult() ? 79 : 97);
        return result;
    }
}
2.2 深度研究@EqualsAndHashCode

为了搞清楚具体配置,我觉得还是有必要看看@EqualsAndHashCode注解的源码内容的:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
	// 排除指定字段
	String[] exclude() default {};
	// 包含指定字段,将来会不再使用,使用 @EqualsAndHashCode.Include 代替
	String[] of() default {};
	// 是否在计算之前调用父类的实现
	boolean callSuper() default false;
	// 是否使用 getter
	boolean doNotUseGetters() default false;
	// 在生成的 equals 和 hashCode 的入参添加注解。e.g. @NonNull
    // up to JDK7: @EqualsAndHashCode(onParam=@__({@AnnotationsGoHere}))
    // from JDK8: @EqualsAndHashCode(onParam_={@AnnotationsGohere})
	AnyAnnotation[] onParam() default {};
	// 是否只使用 @EqualsAndHashCode.Include 标注的字段
	boolean onlyExplicitlyIncluded() default false;

	@Deprecated
	@Retention(RetentionPolicy.SOURCE)
	@Target({})
	@interface AnyAnnotation {}
    
	@Target(ElementType.FIELD)
	@Retention(RetentionPolicy.SOURCE)
	public @interface Exclude {}

	@Target({ElementType.FIELD, ElementType.METHOD})
	@Retention(RetentionPolicy.SOURCE)
	public @interface Include {
		String replaces() default "";
	}
}

下面我们快速写点demo测试一下在指定字段上添加注解这个配置,其余的配置皆可以参考@toString中的示例配置,以下的代码实例只针对EqualsAndHashCode部分

  • equals()hashCode()的入参加注解
@ToString
@Getter @Setter
@EqualsAndHashCode(
        onParam_= {@NotNull}
)
public class User {
    private String username,password;
    private boolean isAdult;
}

// 编译后:
public class User {
    // 其他省略
    public boolean equals(@NotNull Object o) { }
    protected boolean canEqual(@NotNull Object other) {
        return other instanceof User;
    }
    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $username = this.getUsername();
        int result = result * 59 + ($username == null ? 43 : $username.hashCode());
        Object $password = this.getPassword();
        result = result * 59 + ($password == null ? 43 : $password.hashCode());
        result = result * 59 + (this.isAdult() ? 79 : 97);
        return result;
    }
2.3 @EqualsAndHashCode 全局配置
# 是否指定使用 getter 方法获取字段属性值,默认是使用 getter
lombok.equalsAndHashCode.doNotUseGetters = [true | false] (default: false)
# 是否在计算之前调用父类的实现
lombok.equalsAndHashCode.callSuper = [call | skip | warn] (default: warn)
# 是否禁用 @EqualsAndHashCode
lombok.equalsAndHashCode.flagUsage = [warning | error] (default: not set)

如果有疑问请留言!!!

参考文档

相关文章