面试题答案
一键面试构建通用数据处理和转换框架
- 定义泛型接口和类:
- 首先定义通用的数据处理接口,例如:
public interface DataProcessor<T, R> { R process(T data); }
- 这里
T
是输入数据类型,R
是输出数据类型。在业务逻辑层和数据持久层等,可以根据具体需求实现这个接口。例如:
public class UserDataProcessor implements DataProcessor<User, UserDTO> { @Override public UserDTO process(User user) { // 将 User 对象转换为 UserDTO 对象的逻辑 UserDTO userDTO = new UserDTO(); userDTO.setId(user.getId()); userDTO.setName(user.getName()); return userDTO; } }
- 构建通用的处理类:
- 可以创建一个通用的处理类来管理数据处理流程。
public class DataTransformer<T, R> { private DataProcessor<T, R> processor; public DataTransformer(DataProcessor<T, R> processor) { this.processor = processor; } public R transform(T data) { return processor.process(data); } }
- 在使用时:
User user = new User(); // 初始化 user 对象 UserDataProcessor userDataProcessor = new UserDataProcessor(); DataTransformer<User, UserDTO> transformer = new DataTransformer<>(userDataProcessor); UserDTO userDTO = transformer.transform(user);
保证数据类型安全
- 编译时检查:Java 泛型在编译时会进行类型检查,确保传递和处理的数据类型符合定义。例如,上述
DataTransformer
类在实例化时,如果传递的DataProcessor
实现类的输入输出类型与DataTransformer
声明的类型不一致,编译器会报错。 - 类型参数约束:可以使用类型参数的上界来进一步约束类型。例如,如果所有的数据处理逻辑都需要处理实现了
Serializable
接口的数据,可以这样定义接口:public interface DataProcessor<T extends Serializable, R extends Serializable> { R process(T data); }
- 这样在实现接口时,传入的类型必须是
Serializable
的子类型,保证了数据处理过程中的类型安全。
- 这样在实现接口时,传入的类型必须是
考虑泛型擦除带来的问题及解决方案
- 泛型擦除问题:Java 的泛型是在编译时进行类型检查和类型推断的,运行时会擦除泛型类型信息。这可能导致在运行时无法获取泛型的实际类型。例如:
List<String> stringList = new ArrayList<>(); List<Integer> integerList = new ArrayList<>(); System.out.println(stringList.getClass() == integerList.getClass());
- 上述代码输出
true
,因为运行时List<String>
和List<Integer>
的实际类型都是ArrayList
,泛型类型信息被擦除。
- 上述代码输出
- 解决方案:
- 使用类型令牌:可以通过传递一个代表泛型类型的
Class
对象来解决部分问题。例如,在DataTransformer
类中添加获取泛型类型的方法:
public class DataTransformer<T, R> { private DataProcessor<T, R> processor; private Class<T> inputType; private Class<R> outputType; public DataTransformer(DataProcessor<T, R> processor, Class<T> inputType, Class<R> outputType) { this.processor = processor; this.inputType = inputType; this.outputType = outputType; } public Class<T> getInputType() { return inputType; } public Class<R> getOutputType() { return outputType; } public R transform(T data) { return processor.process(data); } }
- 在使用时:
UserDataProcessor userDataProcessor = new UserDataProcessor(); DataTransformer<User, UserDTO> transformer = new DataTransformer<>(userDataProcessor, User.class, UserDTO.class); Class<User> inputType = transformer.getInputType(); Class<UserDTO> outputType = transformer.getOutputType();
- 通过反射获取类型信息:在某些情况下,结合反射可以在运行时处理泛型相关的操作。例如,在数据持久层,如果需要根据泛型类型进行数据库操作,可以使用反射来创建相应的数据库操作对象。假设我们有一个通用的数据库操作接口
DatabaseOperator
:
public interface DatabaseOperator<T> { void save(T entity); T findById(int id); }
- 可以通过反射来实例化具体的数据库操作实现类:
public class DatabaseUtil<T> { private Class<T> entityClass; public DatabaseUtil(Class<T> entityClass) { this.entityClass = entityClass; } public DatabaseOperator<T> getDatabaseOperator() { try { // 假设具体实现类的命名规则为 "实体类名 + DatabaseOperatorImpl" String className = entityClass.getSimpleName() + "DatabaseOperatorImpl"; Class<?> operatorClass = Class.forName(className); return (DatabaseOperator<T>) operatorClass.newInstance(); } catch (Exception e) { throw new RuntimeException("Failed to create database operator", e); } } }
- 在使用时:
DatabaseUtil<User> userDatabaseUtil = new DatabaseUtil<>(User.class); DatabaseOperator<User> userOperator = userDatabaseUtil.getDatabaseOperator(); User user = new User(); userOperator.save(user);
- 使用类型令牌:可以通过传递一个代表泛型类型的
通过以上方式,可以构建一个基于 Java 泛型的通用数据处理和转换框架,保证数据类型安全且具有高度的可扩展性,同时有效应对泛型擦除带来的问题。