Java泛型类中如何设置方法仅对指定的子类类型可见
问题原因
你当前代码的核心问题是方法上重新声明的<T extends Patient>是方法级独立泛型参数,和类定义的泛型T完全无关,相当于遮蔽了类的泛型声明。只要调用时传入的第一个参数是Patient类型,不管UserDao实例的泛型是Admin还是Patient,都能通过编译,所以才会出现adminUserDao也能调用该方法的问题。
解决方案
推荐优先使用子类分层的实现方式,符合面向对象的开闭原则,逻辑清晰无额外hack成本:
方案1:拆分Dao为基类+子实现类
- 抽象通用UserDao基类,仅存放Admin和Patient共用的方法:
public abstract class UserDao<T extends User> { protected Connection connection; public UserDao(Connection connection) { this.connection = connection; } // 存放两类用户通用的Dao方法,例如按ID查询用户、更新基础用户信息等 }
- 实现Patient专属Dao,存放Patient特有的业务方法:
public class PatientDao extends UserDao<Patient> { public PatientDao(Connection connection) { super(connection); } public boolean addPrescription(Patient patient, Prescription prescription) { boolean isAdded = false; int patientId = patient.getId(); ArrayList<Item> items = prescription.getItems(); ItemDao itemDao = new ItemDao(connection); try { PreparedStatement ps = connection.prepareStatement(Constants.ADD_PATIENT_ID_PRESCRIPTIONS_QUERY); ps.setInt(1, patientId); int result = ps.executeUpdate(); if (result == 1) { for (var item : items) { isAdded = itemDao.addItem(item); } } } catch (SQLException e) { e.printStackTrace(); } return isAdded; } }
- 同理实现Admin专属Dao,存放Admin特有的业务方法。
- 调用示例:
PatientDao patientDao = new PatientDao(DbConnection.getConnection()); AdminDao adminDao = new AdminDao(DbConnection.getConnection()); Patient patient = new Patient(); Prescription prescription = new Prescription(); // 仅PatientDao实例可调用addPrescription方法 patientDao.addPrescription(patient, prescription); // 以下代码编译直接报错,AdminDao不存在该方法 // adminDao.addPrescription(patient,prescription);
方案2:不拆分类的编译期限制方案
如果不想拆分Dao类,可以通过泛型边界绑定实现编译期限制,仅当类的泛型T是Patient或其子类时可调用该方法:
public class UserDao<T extends User> { private Connection connection; public UserDao(Connection connection) { this.connection = connection; } // 泛型边界绑定,仅当T继承Patient时可通过编译 public <U extends Patient, R extends T & U> boolean addPrescription(R patient, Prescription prescription) { boolean isAdded = false; int patientId = patient.getId(); ArrayList<Item> items = prescription.getItems(); ItemDao itemDao = new ItemDao(connection); try { PreparedStatement ps = connection.prepareStatement(Constants.ADD_PATIENT_ID_PRESCRIPTIONS_QUERY); ps.setInt(1, patientId); int result = ps.executeUpdate(); if (result == 1) { for (var item : items) { isAdded = itemDao.addItem(item); } } } catch (SQLException e) { e.printStackTrace(); } return isAdded; } }
这种方式不需要拆分类,但泛型声明相对晦涩,维护成本更高,仅建议小项目快速迭代时使用。
内容的提问来源于stack exchange,提问作者Ali Habibian
相关产品推荐
相关产品推荐

