簡體   English   中英

用Java調用自身的構造函數

[英]A constructor calling itself in java

我的課堂上有兩個構造函數,它們擴展了另一個課堂。 第一個有兩個參數。 第二個不帶參數,並以this(param1,param2)開頭,后者調用第一個構造函數。 在帶有兩個參數的第一個構造函數中,我有一個if語句來檢查某些條件。 如果滿足條件,則可以,程序可以將值分配給兩個變量。 否則,我希望第一個構造函數再次被調用,直到滿足這些條件。 能做到嗎 我的目標是將一個學生添加到數組中,並打印出10個年齡在18歲以上的學生,並且不早於1980年且不遲於2015年開始上學。

這是子類

public class Student extends Human{
    private int startYear;
    private int currentYear;

    public Student(int startYear, int currentYear){
        super(); // the constructor with no argument is called (i.e. the constructor that slumps age and name)
        if(Integer.parseInt(this.getAge())>18 && this.startYear<=2015 && this.startYear>=1980){
            this.startYear = startYear;
            this.currentYear = currentYear;
        } else {
            // What do I put here to call this constructor again?
        }
    }

    public Student(){
        this((int)(Math.random()*2011),(int)(Math.random()*4));
    }

這是超類

public class Human {
    private int age;
    private String name;

    /**
     * creates a list of names
     */
    private static String[] nameArray = {"Name1", "Name2", "Name3"};

    /**
     * This is a constructor
     * @param ageIn
     * @param nameIn
     */
    public Human(int ageIn, String nameIn){
        this.age = ageIn;
        this.name = nameIn;
    }

    /**
     * constructor that does not take any parameters
     * this() refers to the "head constructor" in this class.
     */
    public Human(){
        this((int)(Math.random()*100), nameArray[(int)(Math.random()*nameArray.length)]);
}
/**
 * create setter
 * @param ageIn
 */
public void setAge(int ageIn){
    this.age = ageIn;
}

/**
 * create setter
 * @param nameIn
 */
public void setName(String nameIn){
    this.name = nameIn;
}

/**
 * returns a string with name and age
 * @return
 */
public String toString(){
    return "Name: " + this.name + ", " + "Age: " + this.age;
}

我相信您有設計上的問題。

您似乎正在嘗試為“ Human和“ Students創建構造函數,這些構造函數將為各個字段設置具有隨機值的對象。

首先,這個目標本身值得懷疑。 設計對象時,不僅要考慮一項任務。 對象應設計為以通常有用的方式表示真實世界的實體。 構造函數通常應具有可預測的結果。 如果未提供值,他們可以設置合理的默認值。

因此,這將是更好的設計HumanStudent沒有隨機性-與接受來電者給定的值平原構造-然后設計一個單獨的方法,將產生,這將是用於填充數組特定任務有用的隨機值。

這意味着您的構造函數應僅驗證HumanStudent可接受的值。 通常,如果將參數傳遞給對該構造函數無效的構造函數,則該構造函數將引發異常。 常用的異常是IllegalArgumentException ,它是RuntimeException ,不需要在構造函數的throws子句中聲明。

因此,讓我們從Human開始。

public class Human {
    private int age;
    private String name;

    /**
     * This is a constructor
     * @param ageIn
     * @param nameIn
     */
    public Human(int ageIn, String nameIn){
        if ( ageIn < 0 ) {
            throw new IllegalArgumentException( "Negative age is not acceptable for human: " + ageIn );
        }
        if ( ageIn > 150 ) {
            throw new IllegalArgumentException( "Age is limited to 150 years for a human, supplied value was: " + ageIn );
        }
        if ( nameIn == null ) {
            throw new IllegalArgumentException( "A non-null string is expected for human" );
        }
        age = ageIn;
        name = nameIn;
    }

    // Getters, setters
}

現在,該類甚至沒有no-args構造函數,因為暫時而言, Human沒有“合理的”默認值。 每當您創建一個時,年齡和名稱都是必填項。

如您所見,我正在檢查年齡是否在合理范圍內,並且該名稱不為null。 如果參數不合理,將引發異常,並且對象創建將失敗。

接下來,轉到學生。 Student似乎是18歲以上的人,他在1980年至2015年之間開始學習。

public class Student extends Human{
    private int startYear;
    private int currentYear;

    public Student( int age, String name, int startYear, int currentYear ) {

        super( age, name );
        if ( age < 18 ) {
            throw new IllegalArgumentException( "A student cannot be less than 18 years old. Supplied age is: " + age );
        }
        if ( startYear < 1980 || startYear > 2015 ) {
            throw new IllegalArgumentException( "A Student must have started studying between 1980 and 2015. Supplied start year is: " + startYear );
        }
        if ( currentYear < 1 || currentYear > 4 ) {
            throw new IllegalArgumentException( "A student must currently be in his 1st to 4th year. Supplied curret year is: " + currentYear );
        }
        this.startYear = startYear;
        this.currentYear = currentYear;
    }

    // Getters, setters, etc

}

現在您可能會注意到,我們將年齡原樣傳遞給super(age,name)並僅之后測試約束。 這是因為super(...)必須是構造函數中的第一條指令。 但這並不重要。 不是為super分配了新對象。 分配已經完成,因為super是當前對象的一部分。 因此,如果年齡小於18歲,那么超級構造函數可能已經做過幾次任務,這些任務無論如何都不會使用,因為事實證明該年齡對學生而言是非法的,因此它將被銷毀-但這並不是很多浪費,無論如何都無法避免。

其他檢查是針對“ Student本身中的字段的,因此它們會在作業完成之前出現。

現在,我們如何為您的工作分配合適的“隨機”學生?

如果您認為在其他作業中將有用的話,可以在Student創建一個靜態方法,或者在您為當前作業編寫的一個類中創建它,也許是Main類,或者Utility類等。

無論我們在哪里添加它,我們都希望在該類中具有可能名稱的靜態數組:

private static final String[] namesForRandomStudents = { "John Smith",
                                                         "George Robinson",
                                                         "Sarah Carpenter",
                                                         "Judy Thatcher"
                                                         // etc
                                                       };

也許添加一個Random對象,該對象將在內部用於生成學生姓名:

private static final Random studentRandomGenerator = new Random();

現在,您可以聲明您的靜態工廠方法(用於生成特定類實例的靜態方法稱為靜態工廠方法)。

天真的,您可能認為最好的方法是只生成隨機的年齡和年份,將它們傳遞給構造函數並捕獲異常,直到成功生成“好”結果為止:

/**
 * NAIVE APPROACH, NOT RECOMMENDED
 */
public static Student getRandomStudentInstance() {

    boolean failed = true;
    Student result = null;
    do {
        try {
            result = new Student(
                            studentRandomGenerator.nextInt(),
                            namesForRandomStudents[studentRandomGenerator.nextInt(namesForRandomStudents.length)],
                            studentRandomGenerator.nextInt(),
                            studentRandomGenerator.nextInt());
            failed = false;
        } catch ( IllegalArgumentException e ) {
            // Nothing to do here, failed will be true so
            // we'll attempt to generate again
        }
    } while ( failed );

    return result;
}

但這是不好的。 在成功之前,您可能會創建大量將失敗的對象。 這將花費大量時間,這還因為捕獲機制本身比僅循環等要重。

通常,如果拋出RuntimeException (在這種情況下為IllegalArgumentException ),則意味着您應該傳遞值之前檢查該值。 在這種情況下也是如此。 最好確保您發送的合理值根本不會引起異常:

/**
 * Improved version
 */
public static Student getRandomStudentInstance() {

    // Generate an age between 18 and 40
    int age = studentRandomGenerator.nextInt(23) + 18;

    // Generate a name from the array:
    String name = namesForRandomStudents[studentRandomGenerator.nextInt(namesForRandomStudents.length)];

    // Generate a start year between 2000 and 2015 inclusive
    int startYear = studentRandomGenerator.nextInt(16) + 2000;

    // Generate student's current year between 1 and 4 inclusive
    int currentYear = studentRandomGenerator.nextInt(4) + 1;

    return new Student(age,name,startYear,currentYear);
}

現在您知道這些值是合理的。 您可以將選擇范圍擴展到整個法律范圍,我只是選擇了“看起來不錯”的值,沒有百歲老人或在里根時代就開始學習的學生。

您可以根據需要多次調用此工廠方法,以填充隨機學生等的列表,並且由於將“生成”方法與構造函數分開,因此可以使用StudentHuman類在將來的任務中,這些任務將不依賴於隨機生成,而是例如從數據庫中填充值。

沒有參數的構造函數應該首先調用super(); 如果沒有給出,編譯器將完成。 如果您有第二個構造函數,並且有參數,則第一個調用應該是this(); 也就是說,對另一個構造函數的調用。 (來源,OCA)

構造函數僅被調用一次:創建類實例時。 您可以在其中放置一個檢查特定條件的循環。 在該循環中,您可以根據需要多次調用一個方法。

因此,讓工作在方法內完成,並讓構造函數調用方法,如果需要的話,可以使用循環多次。

這是不可能的。 調用super()您將創建帶有某些隨機參數的Human對象。

因此,如果Student班不滿足您的條件,則需要引發異常。

我認為您應該將代碼更改為:

super(); // the constructor with no argument is called (i.e. the constructor that slumps age and name)
if(Integer.parseInt(this.getAge())>18 && this.startYear<=2015 && this.startYear>=1980){
    this.startYear = startYear;
    this.currentYear = currentYear;
} else {
   throw new IllegalArgumentException();
}

那么,您想生成隨機學生嗎?

構造函數應保留為構造函數,並且僅分配變量。

public class Student extends Human{
    private int startYear;
    private int currentYear;

    /*
     * Just a classic Student constructor with parameters
     */
    public Student(int startYear, int currentYear){
        this.startYear = startYear;
        this.currentYear = currentYear;
    }


    /*
     * Generate a random student aged over 18 and started school between 1980 and 2015.
     */
    public static Student generateRandomStudent(){
       // random age>18
       int age = 18 + Math.random()*(100-18);
       // random name chosen in the Human array
       String name = Human.nameArray[(int)(Math.random()*nameArray.length)]
       // random startYear between 1980 and 2015
       int startYear = 1980 + Math.random()*(2015-1980);
       // What the purpose of currentYear ?
       int currentYear = 2015;

       // Create the desired student
       Student randomStudent = new Student(startYear, currentYear)
       randomStudent.setAge(age);
       randomStudent.setName(name);

       return randomStudent;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM