强制要求调用所有方法

时间:2016-01-15 08:46:49

标签: java

我经常使用对象来存储从数据库中获取的实体的属性。我在对象中使用私有变量,然后使用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;
}

}

4 个答案:

答案 0 :(得分:2)

这是一个建议:

  • 定义包含所有字段(类别,开放,结束,持续时间等)的“数据”类,例如InternshipData
  • Internship类中,定义一个setter方法:setData(InternshipData data)。只要您需要配置Internship
  • ,请使用此setter
  • 在setter中,检查提供的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。