在Java Web开发中,统一封装API返回数据是一个常见的需求。我们通常会定义一个通用的Result类,用于包装成功或失败时的返回数据、状态码和消息。然而,在使用泛型时,可能会遇到类型不匹配的问题,例如:
@PostMapping("/register")
public Result<Map<String, Object>> register(@RequestBody RegisterDTO dto) {
try {
// 成功返回 Map<String, Object>
Map<String, Object> resp = new HashMap<>();
resp.put("token", token);
resp.put("user", user);
return Result.ok(resp);
} catch (Exception e) {
// 错误返回 Result<Object>,导致类型不匹配
return Result.error(e.getMessage()); // 编译错误
}
}错误提示:
Required type: Result<Map<String, Object>>
Provided: Result<Object>本文将分析该问题的原因,并提供一种更灵活的Result设计,使其能适应各种返回类型,同时保持类型安全。
Result类的问题原始Result类的部分代码如下:
public class Result<T> implements Serializable {
// ... 其他字段
public static Result<Object> ok(Object data) {
Result<Object> r = new Result<>();
r.setResult(data);
return r;
}
public static Result<Object> error(String msg) {
Result<Object> r = new Result<>();
r.setMessage(msg);
r.setSuccess(false);
return r;
}
}问题在于:
ok() 和 error() 返回的是 Result<Object>,无法自动适配 Result<Map<String, Object>>。Result<Map<String, Object>> 时,error() 返回 Result<Object>,导致类型不匹配。Java的泛型在编译后会进行类型擦除(Type Erasure),但编译时仍然会进行类型检查。因此,如果方法返回 Result<Map<String, Object>>,就必须保证所有返回路径都匹配该类型,否则会编译失败。
Result类我们可以将静态方法改为泛型方法,使其能自动适配调用处的泛型类型:
public static <T> Result<T> ok(T data) {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(data);
return r;
}
public static <T> Result<T> error(String msg) {
Result<T> r = new Result<>();
r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
r.setMessage(msg);
r.setSuccess(false);
return r;
}Result类@Data
@ApiModel(value = "接口返回对象", description = "接口返回对象")
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "成功标志")
private boolean success = true;
@ApiModelProperty(value = "返回处理消息")
private String message = "操作成功!";
@ApiModelProperty(value = "返回代码")
private Integer code = 0;
@ApiModelProperty(value = "返回数据对象")
private T result;
@ApiModelProperty(value = "时间戳")
private long timestamp = System.currentTimeMillis();
// 静态方法(泛型方法)
public static <T> Result<T> ok() {
return ok(null);
}
public static <T> Result<T> ok(T data) {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(data);
return r;
}
public static <T> Result<T> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}
public static <T> Result<T> error(int code, String msg) {
Result<T> r = new Result<>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}
// 链式调用方法
public Result<T> success(String message) {
this.message = message;
this.code = CommonConstant.SC_OK_200;
this.success = true;
return this;
}
public Result<T> fail(String msg) {
this.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
this.setMessage(msg);
this.setSuccess(false);
return this;
}
}register方法@PostMapping("/register")
public Result<Map<String, Object>> register(@RequestBody RegisterDTO dto) {
try {
User user = userService.register(dto.getUsername(), dto.getPassword(), dto.getEmail());
String token = userService.login(user);
Map<String, Object> resp = new HashMap<>();
resp.put("token", token);
resp.put("user", user);
return Result.ok(resp); // 返回 Result<Map<String, Object>>
} catch (Exception e) {
return Result.error(e.getMessage()); // 现在返回的也是 Result<Map<String, Object>>
}
}使用 <T> 声明泛型方法,使返回值类型与调用处匹配。
例如:
public static <T> Result<T> ok(T data) {
Result<T> r = new Result<>();
r.setResult(data);
return r;
}
这样,Result.ok(map) 会自动返回 Result<Map<String, Object>>。
优化前可能有多个ok()方法:
public static Result<Object> ok(Object data) { ... }
public static Result<Map<String, Object>> ok(Map<String, Object> data) { ... }优化后只需一个泛型方法即可覆盖所有情况。
保留非静态方法,支持链式调用:
return new Result<Map<String, Object>>()
.success("注册成功")
.good(data);@Data
@ApiModel(value = "接口返回对象", description = "接口返回对象")
public class Result<T> implements Serializable {
@ApiModelProperty(value = "返回数据对象")
private T result;
// ...
}确保Swagger文档能正确显示泛型类型。
public static <T> Result<T> error(int code, String msg) {
Result<T> r = new Result<>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}可以自定义HTTP状态码,如 Result.error(400, "参数错误")。
优化前 | 优化后 |
|---|---|
Result<Object> 固定返回 | Result<T> 动态适配 |
多个ok()方法冗余 | 单个泛型方法覆盖所有情况 |
错误返回类型不匹配 | 错误返回也能匹配泛型 |
通过这种方式,Result类可以更灵活地适应各种返回类型,同时保持编译时的类型安全。