views:

182

answers:

3

I have one class called Person that basically looks like:

public class Person
{
    String firstName;
    String lastName;
    String telephone;
    String email;

    public Person()
    {
       firstName = "";
       lastName = "";
       telephone = "";
       email = "";
    }

    public Person(String firstName, String lastName, String telephone, String email) 
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.telephone = telephone;
        this.email = email;
    }

    public String getFirstName()
    {
        return firstName;
    }

    public void setFirstName(String firstName)
    {
        this.firstName = firstName;
    }
 ....

Using that class, I setup an abstract class called Loan that looks like:

public abstract class Loan
{   
    public void setClient(Person client)
    {
        this.client = client;
    }

    public Person getClient()
    {
        return client;
    }

    public void setLoanId(int nextId)
    {
        loanId = nextId;
        nextId++;
    }

    public int getLoanId()
    {
        return loanId;
    }

    public void setInterestRate(double interestRate)
    {
        this.interestRate = interestRate;
    }

    public double getInterestRate()
    {
        return interestRate;
    }

    public void setLoanLength(int loanLength)
    {
        this.loanLength = loanLength;
    }

    public int getLoanLength()
    {
        return loanLength;
    }

    public void setLoanAmount(double loanAmount)
    {
        this.loanAmount = loanAmount;
    }

    public double getLoanAmount(double loanAmount)
    {
        return loanAmount;
    }

    private Person client;
    private int loanId;
    private double interestRate;
    private int loanLength;
    private double loanAmount;
    private static int nextId = 1;

}

I have to extend the Loan class with CarLoan and it looks like:

public class CarLoan extends Loan
{
    public CarLoan(Person client, double vehiclePrice, double downPayment, double salesTax,
                    double interestRate, CAR_LOAN_TERMS length)
    {
        super.setClient(client);
        super.setInterestRate(interestRate);
        this.client = client;
        this.vehiclePrice = vehiclePrice;
        this.downPayment = downPayment;
        this.salesTax = salesTax;
        this.length = length;

    }

    public void setVehiclePrice(double vehiclePrice)
    {
        this.vehiclePrice = vehiclePrice;
    }

    public double getVehiclePrice()
    {
        return vehiclePrice;
    }

    public void setDownPayment(double downPayment)
    {
        this.downPayment = downPayment;
    }

    public double getDownPayment()
    {
        return downPayment;
    }

    public void setSalesTax(double salesTax)
    {
        this.salesTax = salesTax;
    }

    public double getSalesTax()
    {
        return salesTax;
    }

    public String toString()
    {
        return getClass().getName() + "[vehiclePrice = " + vehiclePrice + '\n' 
                                        + "downPayment = " + downPayment + '\n'
                                        + "salesTax = " + salesTax 
                                        + "]";
    }

    public enum CAR_LOAN_TERMS {TWO_YEAR, THREE_YEAR, SIX_YEAR};
    private double vehiclePrice;
    private double downPayment;
    private double salesTax;

Few questions.

(a) Is what I did in the Loan class to setClient correct given what I have in the Person class? (e.g.this.client = client)

(b) Can I call super twice in a method? I have to set two attributes from the Loan class from the constructor in the CarLoan class and I thought that would be a way to do it.

(c) Do you have to set attributes for enumeration types differently in a constructor or getter/setter methods? I get an error for (this.length = length) in my CarLoan class and I was unsure of how enumeration values should be set. Thanks!

+1  A: 

OK, in order:

  1. setClient Looks perfectly fine. Nothing wrong with it. However, you want to avoid setting this.client directly in the CarLoan constructor—you're already calling setClient (thanks to @Gabriel and @Aeth).
  2. Sure, you can use super to access the parent class methods as much as you want. What you need to be careful with is calling the superclass' constructor, which you can only do once and at the beginning of the subclass' constructor. super != super().
  3. Nope, this.length = length is fine. The problem is that you don't have a field called length. You might want to add one of those.
Samir Talwar
1. does not look so fine, the client field is set twice and I am not sure if it is ok to call non-static method in a constructor.
Gabriel Ščerbák
@Gabriel: It's not set twice, as far as I can see, and constructors are run during instantiation, which means `this` exists, and so non-static methods are perfectly valid and, in fact, encouraged.
Samir Talwar
@Samir Talwar first there is the setClient call and then direct client assignment.
Gabriel Ščerbák
@Samir: The `client` of `Loan` is set twice in the constructor of `CarLoan`. It is set once in the first line with the call to `super.setClient(client)` and again in the third line with `this.client = client`.
Matthew T. Staebler
@Gabriel: It is not inherently incorrect to call a non-static method from a constructor. It is recommended that only final non-static methods are called however. If you call a non-final non-static method, then a subclass of `CarLoan` can override the method, and in that method attempt to access fields that have not been created yet because the subclass instance has not been created yet.
Matthew T. Staebler
@Aeth oh, thank you very much, now I remember, I read about this before, really thanks for the flashback:)
Gabriel Ščerbák
@Gabriel, @Aeth: Thanks, I see it now. I'll edit the answer to match.
Samir Talwar
+1  A: 

1) It is conventional to put the declarations of the attributes of a class before the constructors and methods.

2) The statement this.client = client; in the CarLoan class will give you a compilation error because the client field is declared as private in the Loan class. (And that statement is redundant anyway because you just initialized the same field using the setter ... though expect you already knew that.)

3) A better way to initialize the fields of a superclass is to pass the arguments to the superclass constructor. For example:

public abstract class Loan
{   
    private Person client;
    private double interestRate;

    public Loan(Person client, double interestRate) {
        this.client = client;
        this.interestRate = interestRate;
    }
    ...
}

public class CarLoan extends Loan
{   
    ...

    public CarLoan(Person client, double vehiclePrice, double downPayment, double salesTax,
                    double interestRate, CAR_LOAN_TERMS length)
    {
        super(client, interestRate); 
        this.vehiclePrice = vehiclePrice;
        ...
    }
}

The reason this approach is better is that the Loan class takes responsibility for its initialization, and doesn't rely on the various subclass constructors doing the job. (If you add an extra field to Loan and add the corresponding parameter to the Loan constructor, the compiler reminds you to modify all of the subclass constructors to provide the initial value in the super constructor chaining. If the subclasses are responsible for setting fields in the base class during initialization, then the compiler won't notice that you forgot to add the new setter call.)

4) If you do call methods in a constructor, it is good practice to ensure that they cannot be overridden in a subclass. (No ... it is not entirely wrong for the methods to be overridden, but there are things that can go horribly wrong. Calling potentially overridable methods in a constructor makes your code fragile.)

5) If this was production code, use of float or double to represent currency values would be a big no-no!

Stephen C
A: 

Answering question (c), I think you're getting the error because you need to define length down there along with the variables you've already defined.

Nick