错误复现
这个报错是在学习微服务的过程中发现的,涉及到 Feign 远程调用。
一共有两个服务:user 和 user2。
user 的 Feign 接口如下:
java
@FeignClient("user")
public interface UserClient {
@GetMapping("/user/getUser")
Result getUser(@RequestParam("id") int id);
}
接口实现如下:
java
@GetMapping("/user/getUser")
@Override
public Result getUser(int id) {
return Result.success(new User("李华", 18));
}
Result 是通用的返回类:
java
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result {
private int status;
private String msg;
private Object data;
public static Result success(Object data) {
return new Result(1, null, data);
}
public static Result success() {
return new Result(1, null, null);
}
}
然后,在 user2 服务中调用了 user 的 Feign 接口:
java
@GetMapping("/user2/getUser")
public Result getUser(int id) {
Result result = userClient.getUser(id);
Object data = result.getData();
User user = (User) data;
return Result.success(user);
}
结果就报错了:

在 User user = (User) data 这里的强制类型转换错误了。
这里我就不懂了,Result.success(new User("李华", 18)),Object data = new User("李华", 18),从接口调用方得到的 data 也是 Oject 类型啊,Object data = result.getData()。
不应该是 Object data = new User("李华", 18),然后 User user = (User) data,这不是标准的向上转型和向下转型吗?为什么这个时候把 Object 转成 User 就报错了呢?
原因分析
问过 AI 才知道,因为这个对象跨服务了。
这个 User 对象从一个服务传输到了另一个服务,那这个 User 对象是怎么传输过去的呢?
加了 @RestController 注解,User 对象就是被转成 JSON 字符串传输过去的,然后被接口调用方从 JSON 解析得到 Result 对象。
关键就在这里,User 对象被转成了 JSON 字符串,JSON 会记录这个 User 对象的类型吗?并不会吧,所以 JSON 被解析后再转成 User 对象就报错了。
不对吧,如果 JSON 不知道这个 User 对象的类型,是怎么获取 User 的属性呢?{"status": 1, "msg": null, "data": {"name": "李华", "age": 18}},data 是 Object 类型啊,如果不知道 data 是 User 类型怎么知道有 name 和 age 呢?
在 java 对象 -> JSON 字符串的时候,因为 data = new User("李华", 18),框架中的 JSON 解析器可以通过反射获取 data 指向的 User 对象的属性,所以这个接口返回的 JSON 字符串是包含 User 对象的 name 和 age 的。
当这个 JSON 对象来到接口调用方的时候,因为在 UserClient 中已经确定了 getUser 这个接口返回的就是 Result 对象,所以 JSON 解析器会将 {"status": 1, "msg": null, "data": {"name": "李华", "age": 18}} 这个字符串转成 Result 对象。
这个 JSON 没有把 User 写在额头上吧,所以 JSON 解析器会把 {"name": "李华", "age": 18} 转成 Object,而不是转成 User 对象。
但是呢,JSON 解析器也不清楚 {"name": "李华", "age": 18} 到底是什么对象,但是 JSON 都是键值对,所以 JSON 解析器把 {"name": "李华", "age": 18} 转成了 LinkedHashMap<String, Object> 的对象,也就是 Object data = new LinkedHashMap<String, Object>()。
User user = (User) data 就是把 LinkedHashMap 对象转成 User 对象,所以报错了,提示:class java.util.LinkedHashMap cannot be cast to class org.example.model.User。
解决方案
既然现在知道 data 是一个 LinkedHashMap 对象,就可以解决问题了。
方案一:用 LinkedHashMap 的 API 获取 User 属性。
java
@GetMapping("/user2/getUser")
public Result getUser(int id) {
Result result = userClient.getUser(id);
Object data = result.getData();
LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>) data;
User user = new User();
String name = (String) map.get("name");
Integer age = (Integer) map.get("age");
user.setName(name);
user.setAge(age);
return Result.success(user);
}
方案二:用 JSON 解析器转成 User 对象
java
@GetMapping("/user2/getUser")
public Result getUser(int id) {
Result result = userClient.getUser(id);
Object data = result.getData();
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.convertValue(data, User.class);
return Result.success(user);
}
泛型尝试
原来这个报错是因为 JSON 转对象的时候不知道类型啊,那我把 Result 的 data 改成泛型不就行了吗?
好吧,进行一下泛型的尝试:
java
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
private int status;
private String msg;
private T data;
public static <T> Result<T> success(T data) {
return new Result<>(1, null, data);
}
public static Result success() {
return new Result<>(1, null, null);
}
}
如果只改 Result 的话,还是会报错的,因为你没改接口。

接口返回的还是 Result,没有指定返回值的类型。java 是有泛型擦除的,所以接口调用方还是会把 JSON 字符串转成 Result 对象,把 data 字符串转成 LinkedHashMap,令 data = LinkedHashMap 对象,强转还是报错。
需要手动指定返回值的类型才可以自动转换:
java
@GetMapping("/user/getUser")
Result<User> getUser(@RequestParam("id") int id);
java
@GetMapping("/user2/getUser")
public Result<User> getUser(int id) {
Result<User> result = userClient.getUser(id);
User user = result.getData();
return Result.success(user);
}
现在直接获取到的就是 User 对象了,JSON 解析器在底层已经帮你完成转换流程了。
泛型不是会被擦除吗?怎么这里又知道是 User 类型了?
泛型确实会被擦除啊,但是接口方法都写明了返回的是一个 User 类对象,Result<User> result = userClient.getUser(id),JSON 解析器就知道了要把 data 转成 User 对象。
那我如果不需要在 Result 对象中设置 data 呢?
那就让 data 等于 null 就可以了。
java
public static <T> Result<T> success() {
return new Result<>(1, null, null);
}
Controller 层的接口返回 Result<Void> 表示这个接口没有 data,JSON 字符串中 data 为 null。
java
@GetMapping("/void-test")
public Result<Void> voidTest() {
return Result.success();
}
java
{
"status": 1,
"msg": null,
"data": null
}