我经常使用对象来存储从数据库中获取的实体的属性。我在对象中使用私有变量,然后使用getter和setter来设置值。因此,为了初始化对象,我调用了对象的所有setter。但是不可能跟踪所有的制定者并且我经常忘记设置一些变量。有没有办法强制要求制定者。我绝对可以初始化使用构造函数的变量,但使用构造函数来设置10-12属性会使代码看起来很破旧。我希望以这样的方式使用接口和子类,使它们以必要的方式实现接口的所有方法,但这里不实现,而是调用它们。
com.mysql.jdbc.PreparedStatement getInternships = (PreparedStatement) Connection.con.prepareCall("CALL getInternships()");
rs=getInternships.executeQuery();
Internship current;
while(rs.next()){
current=new Internship();
current.setId(Integer.parseInt(rs.getString("id")));
current.setTitle(rs.getString("title"));
current.setCategory(rs.getString("category"));
current.setOpening(rs.getDate("opening"));
current.setClosing(rs.getDate("closing"));
current.setDuration(Integer.parseInt(rs.getString("duration")));
current.setStatus(rs.getString("status"));
current.setApplicants(Integer.parseInt(rs.getString("applicants")));
current.setSeats(Integer.parseInt(rs.getString("seats")));
current.setHired(Integer.parseInt(rs.getString("hired")));
list.add(current);
}
实习班
package internships;
import java.util.Date;
public class Internship {
private String title,category,status,about,eligibility,information;
private int id,duration,applicants,seats,hired;
private Date opening,closing;
/**
* @return the opening
*/
public Date getOpening() {
return opening;
}
/**
* @param opening the opening to set
*/
public void setOpening(Date opening) {
this.opening = opening;
}
/**
* @return the hired
*/
public int getHired() {
return hired;
}
/**
* @param hired the hired to set
*/
public void setHired(int hired) {
this.hired = hired;
}
/**
* @return the seats
*/
public int getSeats() {
return seats;
}
/**
* @param seats the seats to set
*/
public void setSeats(int seats) {
this.seats = seats;
}
/**
* @return the applicants
*/
public int getApplicants() {
return applicants;
}
/**
* @param applicants the applicants to set
*/
public void setApplicants(int applicants) {
this.applicants = applicants;
}
/**
* @return the closing
*/
public Date getClosing() {
return closing;
}
/**
* @param closing the closing to set
*/
public void setClosing(Date closing) {
this.closing = closing;
}
/**
* @return the duration
*/
public int getDuration() {
return duration;
}
/**
* @param duration the duration to set
*/
public void setDuration(int duration) {
this.duration = duration;
}
/**
* @return the category
*/
public String getCategory() {
return category;
}
/**
* @param category the category to set
*/
public void setCategory(String category) {
this.category = category;
}
/**
* @return the status
*/
public String getStatus() {
return status;
}
/**
* @param status the status to set
*/
public void setStatus(String status) {
this.status = status;
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/**
* @param title the title to set
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the about
*/
public String getAbout() {
return about;
}
/**
* @param about the about to set
*/
public void setAbout(String about) {
this.about = about;
}
/**
* @return the eligibility
*/
public String getEligibility() {
return eligibility;
}
/**
* @param eligibility the eligibility to set
*/
public void setEligibility(String eligibility) {
this.eligibility = eligibility;
}
/**
* @return the information
*/
public String getInformation() {
return information;
}
/**
* @param information the information to set
*/
public void setInformation(String information) {
this.information = information;
}
}
答案 0 :(得分:2)
这是一个建议:
InternshipData
Internship
类中,定义一个setter方法:setData(InternshipData data)
。只要您需要配置Internship
。InternshipData
对象的所有字段是否已正确初始化。如果不是,则可以抛出异常,或者只是从setter返回false
并从外部检查返回的值。(注意,如果要将数据传递给Internship
构造函数,可以使用类似的方法)
这使您可以检查单个位置的所有字段(setData
)方法,否则您当前的方法很难处理,因为您有一堆setter并且您不知道订单将从客户端代码中调用它们。
答案 1 :(得分:1)
这是另一个运行时的想法 - 我能想到的唯一编译时答案是引入新的PMD / FindBugs规则,让你的IDE在每次保存时运行它们。
您需要为每个要避免的课程申报"未初始化"值,BitSet
用于跟踪已设置的字段,enum
用于Fields
,可为您计算字段并将字段名称与其对应的BitSet
索引相关联。
// keeps track of initialized fields
private BitSet initialized = new BitSet(Fields.values().length);
// field-names not included here will not be considered included in BitSet
enum Fields {
title,category,status,about,eligibility,information,
id,duration,applicants,seats,hired,
opening,closingopening
}
如果Fields
中的所有字段至少设置了一次,则只会返回true:
public void isFullyInitialized() {
return initialized.cardinality() == initialized.size();
}
这将是一个样本制定者:
/**
* @param opening the opening to set
*/
public void setOpening(Date opening) {
this.opening = opening;
initialized.set(Fields.opening.ordinal()); // <- do this in all setters
}
然后你可以在初始化运行结束时assert(isFullyInitialized())
,如果你没有初始化任何字段,它会抛出一个丑陋的断言错误。它确实有一个运行时成本:每个对象实例BitSet
,方法调用稍慢setXyz()
。
如果将此答案与Grodriguez's answer结合使用,则可以避免使用所有这些位集的每对象开销:让中间对象执行所有记帐(使用位集等),并使用以下方法验证其完整性setData()
操作期间的bitset。如果操作成功,则可以安全地丢弃中间对象。但是,在考虑所需的所有样板代码时,我感到畏缩。
答案 2 :(得分:1)
这种简单的反思有趣吗?
我们将使用您实习班的较轻的变体。使用@NotNull标记最终不应为null的字段。是的,这不适用于原始字段,但除非被构造函数强制,否则您的字段可以具有未定义的状态,这由值null表示。使用原语会掩盖这一事实。
class Internship {
@NotNull
private String name;
private String status;
public Internship() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
这是对注释及其处理器的完整测试,这是一种命名精确的方法validateNotNullFields。它获取对象的每个字段,甚至是超类中定义的私有字段和字段。如果在保持null为值的字段上找到注释,则会抛出IllegalArgumentException。如果你喜欢我的解决方案,你可能想要调整它并正确输出字段名称,甚至选择一个没有例外的解决方案。
public class Test {
public static void main(String... args) {
Internship a = new Internship();
a.setName("Karl");
a.setStatus(null);
validateNotNullFields(a);
System.out.println("ok");
try {
Internship b = new Internship();
b.setName(null);
b.setStatus(null);
validateNotNullFields(b); // throws IllegalStateException!
System.out.println("not ok");
} catch (IllegalStateException ise) {
System.out.println("ok");
}
}
static void validateNotNullFields(Object candidate) throws IllegalStateException {
if (candidate == null) {
throw new IllegalArgumentException("argument candidate must not be null!");
}
Class<?> clazz = candidate.getClass();
while (!Object.class.equals(clazz)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Annotation annotation = field.getAnnotation(NotNull.class);
if (annotation == null) {
continue;
}
field.setAccessible(true);
try {
if (field.get(candidate) == null) {
throw new IllegalStateException(
"Field " + field.getName() + " must not be null at this point!");
}
} catch (IllegalArgumentException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
clazz = clazz.getSuperclass();
}
}
}
答案 3 :(得分:1)
事实上,您没有检查是否已经设置了每个字段......您可以使用每次调用所有字段的方法。没有更多的记忆失误:
static void load(Object target, ResultSet rs) throws Exception {
Class<?> clazz = target.getClass();
ResultSetMetaData rsmd = rs.getMetaData();
for (int i=0; i<rsmd.getColumnCount(); i++) {
Field field = clazz.getDeclaredField(rsmd.getColumnName(i+1));
field.setAccessible(true);
int type = rsmd.getColumnType(i+1);
switch (type) {
case Types.DATE: {
field.set(target, rs.getDate(i+1));
}
case Types.VARCHAR: {
field.set(target, rs.getString(i+1));
}
case Types.BIGINT: {
field.set(target, rs.getInt(i+1));
}
default: {
throw new IllegalArgumentException(
"Unhandled field type: " + type);
}
}
}
}
上面的代码中可能存在拼写错误(我没有尝试过运行它) - 但这相当于一个穷人的JPA:对于ResultSet
几个关键类型中的每一列(添加您自己的!),它会尝试在传入的对象中设置同名的对应字段。假设所有名称和类型都匹配,则不会设置任何字段。
性能可能不是很好(您可以缓存这些字段并避免一次又一次地设置它们) - 但如果您想要更清晰的代码,那么无论如何都会使用JPA。