Sunday, May 28, 2017

Introduction to Java programming, Part 2 (2)

Next steps with objects


Part 1 of this tutorial left off with a Person class that was reasonably useful, but not as useful as it could be. Here, you begin learning about techniques to enhance a class such as Person, starting with the following techniques:

  • Overloading methods
  • Overriding methods
  • Comparing one object with another
  • Using class variables and class methods

You'll start enhancing Person by overloading one of its methods.

Overloading methods

When you create two methods with the same name but with different argument lists (that is, different numbers or types of parameters), you have an overloaded method. At runtime, the JRE decides which variation of your overloaded method to call, based on the arguments that were passed to it.
Suppose that Person needs a couple of methods to print an audit of its current state. I call both of those methods printAudit(). Paste the overloaded method in Listing 1 into the Eclipse editor view in the Personclass:
Listing 1. printAudit(): An overloaded method
public void printAudit(StringBuilder buffer) {
   buffer.append("Name=");
   buffer.append(getName());
   buffer.append(",");
   buffer.append("Age=");
   buffer.append(getAge());
   buffer.append(",");
   buffer.append("Height=");
   buffer.append(getHeight());
   buffer.append(",");
   buffer.append("Weight=");
   buffer.append(getWeight());
   buffer.append(",");
   buffer.append("EyeColor=");
   buffer.append(getEyeColor());
   buffer.append(",");
   buffer.append("Gender=");
   buffer.append(getGender());
}
public void printAudit(Logger l) {
   StringBuilder sb = new StringBuilder();
   printAudit(sb);
   l.info(sb.toString());
}
You have two overloaded versions of printAudit(), and one even uses the other. By providing two versions, you give the caller a choice of how to print an audit of the class. Depending on the parameters that are passed, the Java runtime calls the correct method.
Remember two important rules when you use overloaded methods:

  • You can't overload a method just by changing its return type.
  • You can't have two same-named methods with the same parameter list.

If you violate these rules, the compiler gives you an error.

Overriding methods

When a subclass provides its own implementation of a method that's defined on one of its parent classes, that's called method overriding. To see how method overriding is useful, you need to do some work on anEmployee class. Once you have that class set up, I show you where method overriding comes in handy.

Employee: A subclass of Person

Recall from Part 1 of this tutorial that an Employee class might be a subclass (or child) of Person that has additional attributes such as taxpayer identification number, employee number, hire date, and salary.
To declare the Employee class, right-click the com.makotojava.intro package in Eclipse. Click New > Class... To open the New Java Class dialog box, shown in Figure 1.
Figure 1. New Java Class dialog box

Enter Employee as the name of the class and Person as its superclass. Click Finish, and you can see theEmployee class code in an edit window.
You don't explicitly need to declare a constructor, but go ahead and implement both constructors anyway. With the Employee class edit window having the focus, go to Source > Generate Constructors from Superclass.... In the Generate Constructors from Superclass dialog box (see Figure 2), select both constructors and click OK.
Figure 2. Generate Constructors from Superclass dialog box

Eclipse generates the constructors for you. You now have an Employee class like the one in Listing 2.
Listing 2. The Employee class
package com.makotojava.intro;
public class Employee extends Person {
  public Employee() {
    super();
    // TODO Auto-generated constructor stub
  }
   
  public Employee(String name, int age, int height, int weight,
  String eyeColor, String gender) {
    super(name, age, height, weight, eyeColor, gender);
    // TODO Auto-generated constructor stub
  }
}

Employee as a child of Person

Employee inherits the attributes and behavior of its parent, Person. Add some attributes of Employee's own, as shown in lines 7 through 9 of Listing 3.
Listing 3. The Employee class with Person's attributes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.makotojava.intro;
import java.math.BigDecimal;
public class Employee extends Person {
  private String taxpayerIdentificationNumber;
  private String employeeNumber;
  private BigDecimal salary;
  public Employee() {
    super();
  }
  public String getTaxpayerIdentificationNumber() {
    return taxpayerIdentificationNumber;
  }
  public void setTaxpayerIdentificationNumber(String taxpayerIdentificationNumber) {
    this.taxpayerIdentificationNumber = taxpayerIdentificationNumber;
  }
  // Other getter/setters...
}
Don't forget to generate getters and setters for the new attributes, as you did for in the "Your first Java class" section in Part 1.

Overriding the printAudit() method

Presently you'll abrogate the printAudit() strategy (see Listing 1) that you used to organize the present condition of a Person case. Worker acquires that conduct from Person. In the event that you instantiate Employee, set its qualities, and call both of the over-burdens of printAudit(), the call succeeds. In any case, the review that is delivered doesn't completely speak to an Employee. The printAudit() strategy can't design the credits particular to an Employee, since Person doesn't think about them. 

The arrangement is to supersede the over-burden of printAudit() that takes a StringBuilder as a parameter and add code to print the credits particular to Employee.

With Employee open in the editor window or selected in the Project Explorer view, go to Source > Override/Implement Methods.... In the Override/Implement Methods dialog box, shown in Figure 3, select the StringBuilder overload of printAudit() and click OK.
Figure 3. Override/Implement Methods dialog box

Eclipse generates the method stub for you, and then you can fill in the rest, like so:
@Override
public void printAudit(StringBuilder buffer) {
  // Call the superclass version of this method first to get its attribute values
  super.printAudit(buffer);
  // Now format this instance's values
  buffer.append("TaxpayerIdentificationNumber=");
  buffer.append(getTaxpayerIdentificationNumber());
  buffer.append(","); buffer.append("EmployeeNumber=");
  buffer.append(getEmployeeNumber());
  buffer.append(","); buffer.append("Salary=");
  buffer.append(getSalary().setScale(2).toPlainString());
}
Notice the call to super.printAudit(). What you're doing here is asking the (Person) superclass to exhibit its behavior for printAudit(), and then you augment it with Employee-type printAudit() behavior.
The call to super.printAudit() doesn't need to be first; it just seemed like a good idea to print those attributes first. In fact, you don't need to call super.printAudit() at all. If you don't call it, you must format the attributes from Person yourself in the Employee.printAudit() method, or they won't be included in the audit output.

Comparing objects

The Java language provides two ways to compare objects:

  • The == operator
  • The equals() method

Comparing objects with ==

The == syntax compares objects for equality such that a == b returns true only if a and b have the same value. For objects, this will be the case if the two refer to the same object instance. For primitives, if thevalues are identical.
Suppose you generate a JUnit test for Employee (which you saw how to do in the "Your first Java class" section in Part 1. The JUnit test is shown in Listing 4.
Listing 4. Comparing objects with ==
public class EmployeeTest {
  @Test
  public void test() {
    int int1 = 1;
    int int2 = 1;
    Logger l = Logger.getLogger(EmployeeTest.class.getName());
     
    l.info("Q: int1 == int2?           A: " + (int1 == int2));
    Integer integer1 = Integer.valueOf(int1);
    Integer integer2 = Integer.valueOf(int2);
    l.info("Q: Integer1 == Integer2?   A: " + (integer1 == integer2));
    integer1 = new Integer(int1);
    integer2 = new Integer(int2);
    l.info("Q: Integer1 == Integer2?   A: " + (integer1 == integer2));
    Employee employee1 = new Employee();
    Employee employee2 = new Employee();
    l.info("Q: Employee1 == Employee2? A: " + (employee1 == employee2));
  }
}
Run the Listing 4 code inside Eclipse (select Employee in the Project Explorer view, then choose Run As > JUnit Test) to generate the following output:
Sep 18, 2015 5:09:56 PM com.makotojava.intro.EmployeeTest test
INFO: Q: int1 == int2?           A: true
Sep 18, 2015 5:09:56 PM com.makotojava.intro.EmployeeTest test
INFO: Q: Integer1 == Integer2?   A: true
Sep 18, 2015 5:09:56 PM com.makotojava.intro.EmployeeTest test
INFO: Q: Integer1 == Integer2?   A: false
Sep 18, 2015 5:09:56 PM com.makotojava.intro.EmployeeTest test
INFO: Q: Employee1 == Employee2? A: false
In the first case in Listing 4, the values of the primitives are the same, so the == operator returns true. In the second case, the Integer objects refer to the same instance, so again == returns true. In the third case, even though the Integer objects wrap the same value, == returns false because integer1 and integer2refer to different objects. Think of == as a test for "same object instance."

Comparing objects with equals()

equals() is a method that every Java language object gets for free, because it's defined as an instance method of java.lang.Object (which every Java object inherits from).
You call equals() like this:
a.equals(b);
This statement invokes the equals() method of object a, passing to it a reference to object b. By default, a Java program would simply check to see if the two objects are the same by using the == syntax. Becauseequals() is a method, however, it can be overridden. Compare the JUnit test case in Listing 4 to the one in Listing 5 (which I've called anotherTest()), which uses equals() to compare the two objects.
Listing 5. Comparing objects with equals()
@Test
public void anotherTest() {
  Logger l = Logger.getLogger(Employee.class.getName());
  Integer integer1 = Integer.valueOf(1);
  Integer integer2 = Integer.valueOf(1);
  l.info("Q: integer1 == integer2 ? A: " + (integer1 == integer2));
  l.info("Q: integer1.equals(integer2) ? A: " + integer1.equals(integer2));
  integer1 = new Integer(integer1);
  integer2 = new Integer(integer2);
  l.info("Q: integer1 == integer2 ? A: " + (integer1 == integer2));
  l.info("Q: integer1.equals(integer2) ? A: " + integer1.equals(integer2));
  Employee employee1 = new Employee();
  Employee employee2 = new Employee();
  l.info("Q: employee1 == employee2 ? A: " + (employee1 == employee2));
  l.info("Q: employee1.equals(employee2) ? A : " + employee1.equals(employee2));
}
Running the Listing 5 code produces this output:
Sep 19, 2015 10:11:57 AM com.makotojava.intro.EmployeeTest anotherTest
INFO: Q: integer1 == integer2 ? A: true
Sep 19, 2015 10:11:57 AM com.makotojava.intro.EmployeeTest anotherTest
INFO: Q: integer1.equals(integer2) ? A: true
Sep 19, 2015 10:11:57 AM com.makotojava.intro.EmployeeTest anotherTest
INFO: Q: integer1 == integer2 ? A: false
Sep 19, 2015 10:11:57 AM com.makotojava.intro.EmployeeTest anotherTest
INFO: Q: integer1.equals(integer2) ? A: true
Sep 19, 2015 10:11:57 AM com.makotojava.intro.EmployeeTest anotherTest
INFO: Q: employee1 == employee2 ? A: false
Sep 19, 2015 10:11:57 AM com.makotojava.intro.EmployeeTest anotherTest
INFO: Q: employee1.equals(employee2) ? A : false

A note about comparing Integers

In Listing 5, it should be no surprise that the equals() method of Integer returns true if == returns true. But notice what happens in the second case, where you create separate objects that both wrap the value 1== returns false because integer1 and integer2 refer to different objects; but equals() returns true.
The writers of the JDK decided that for Integer, the meaning of equals() would be different from the default (which, as you recall, is to compare the object references to see if they refer to the same object). For Integerequals() returns true in cases in which the underlying (boxed) int value is the same.
For Employee, you didn't override equals(), so the default behavior (of using ==) returns what you'd expect, because employee1 and employee2 refer to different objects.
For any object you write, then, you can define what equals() means as is appropriate for the application you're writing.

Overriding equals()

You can define what equals() means to your application's objects by overriding the default behavior of Object.equals()— and you can do this in Eclipse. With Employee having the focus in the IDE's source window, select Source > Override/Implement Methods to open the dialog box shown in Figure 4.
Figure 4. Override/Implement Methods dialog box

You need to execute the Object.equals() superclass technique. Along these lines, discover Object in the rundown of techniques to supersede or actualize, select the equals(Object) strategy, and snap OK. Overshadow creates the right code and places it in your source document. 

It bodes well that the two Employee items are equivalent if the conditions of those articles are equivalent. That is, they're equivalent if their qualities — name, and age — are the same.

Autogenerating equals()

Eclipse can generate an equals() method for you based on the instance variables (attributes) that you define for a class. Because Employee is a subclass of Person, you first generate equals() for Person. In the Eclipse Project Explorer view, right-click Person and choose Generate hashCode() and equals(). In the dialog box that opens (see Figure 5), click Select All to include all of the attributes in the hashCode() and equals() methods, and click OK.
Figure 5. Generate hashCode() and equals() dialog box

Eclipse generates an equals() method that looks like the one in Listing 6.
Listing 6. An equals() method generated by Eclipse
@Override
public boolean equals(Object obj) {
  if (this == obj)
    return true;
  if (obj == null)
    return false;
  if (getClass() != obj.getClass())
    return false;
  Person other = (Person) obj;
  if (age != other.age)
    return false;
  if (eyeColor == null) {
    if (other.eyeColor != null)
      return false;
  } else if (!eyeColor.equals(other.eyeColor))
    return false;
  if (gender == null) {
    if (other.gender != null)
      return false;
  } else if (!gender.equals(other.gender))
    return false;
  if (height != other.height)
    return false;
  if (name == null) {
    if (other.name != null)
      return false;
  } else if (!name.equals(other.name))
    return false;
  if (weight != other.weight)
    return false;
  return true;
}
The equals() method generated by Eclipse looks complicated, but what it does is simple: If the object passed in is the same object as the one in Listing 6, equals() returns true. If the object passed in is null (meaning missing), it returns false.
Next, the technique verifies whether the Class items are the same (implying that the go in protest must be a Person question). On the off chance that they are the same, each characteristic estimation of the question gone in is verified whether it matches esteem for-esteem with the condition of the given Person case. On the off chance that the quality esteems are invalid, the equivalents() checks the greatest number of as it can, and if those match, the items are viewed as equivalent. You won't not need this conduct for each program, but rather it works for generally purposes.

Exercises

Now, work through a couple of guided exercises to do even more with Person and Employee in Eclipse.

Exercise 1: Generate an equals() for Employee

Try following the steps in "Autogenerating equals()" to generate an equals() for Employee. Once you have your generated equals(), add the following JUnit test case (which I've called yetAnotherTest()) to it:
@Test
public void yetAnotherTest() {
  Logger l = Logger.getLogger(Employee.class.getName());
  Employee employee1 = new Employee();
  employee1.setName("J Smith");
  Employee employee2 = new Employee();
  employee2.setName("J Smith");
  l.info("Q: employee1 == employee2?      A: " + (employee1 == employee2));
  l.info("Q: employee1.equals(employee2)? A: " + employee1.equals(employee2));   
}
If you run the code, you should see the following output:
Sep 19, 2015 11:27:23 AM com.makotojava.intro.EmployeeTest yetAnotherTest
INFO: Q: employee1 == employee2?      A: false
Sep 19, 2015 11:27:23 AM com.makotojava.intro.EmployeeTest yetAnotherTest
INFO: Q: employee1.equals(employee2)? A: true
In this case, a match on Name alone was enough to convince equals() that the two objects are equal. Try adding more attributes to this example and see what you get.

Exercise 2: Override toString()

Remember the printAudit() method from the beginning of this section? If you thought it was working a little too hard, you were right. Formatting the state of an object into a String is such a common pattern that the designers of the Java language built it into Object itself, in a method called (no surprise) toString(). The default implementation of toString() isn't especially useful, but every object has one. In this exercise, you override toString() to make it a little more useful.
If you suspect that Eclipse can generate a toString() method for you, you're correct. Go back into your Project Explorer and right-click the Person class, then choose Source > Generate toString().... In the dialog box, select all attributes and click OK. Now do the same thing for Employee. The code generated by Eclipse for Employee is shown in Listing 7.
Listing 7. A toString() method generated by Eclipse
@Override
public String toString() {
  return "Employee [taxpayerIdentificationNumber=" + taxpayerIdentificationNumber + ",
      employeeNumber=" + employeeNumber + ", salary=" + salary + "]";
}
The code that Eclipse generates for toString doesn't include the superclass's toString() ( Employee's superclass being Person). You can fix that situation quickly, using Eclipse, with this override:
@Override
public String toString() {
  return super.toString() + "Employee [taxpayerIdentificationNumber=" + taxpayerIdentificationNumber +
    ", employeeNumber=" + employeeNumber + ", salary=" + salary + "]";
}
The addition of toString() makes printAudit() much simpler:
@Override
  public void printAudit(StringBuilder buffer) {
  buffer.append(toString());
}
toString() now does the heavy lifting of formatting the object's current state, and you simply stuff what it returns into the StringBuilder and return.
I recommend always implementing toString() in your classes, if only for support purposes. It's virtually inevitable that at some point, you'll want to see what an object's state is while your application is running, and toString() is a great hook for doing that.

Class members

Every object instance has variables and methods, and for each one, the exact behavior is different, because it's based on the state of the object instance. The variables and methods that you have on Personand Employee are instance variables and methods. To use them, you must either instantiate the class you need or have a reference to the instance.
Classes can also have class variables and methods — known as class members. You declare class variables with the static keyword. The differences between class variables and instance variables are:

  • Every instance of a class shares a single copy of a class variable.
  • You can call class methods on the class itself, without having an instance.
  • Class methods can access only class variables.
  • Instance methods can access class variables, but class methods can't access instance variables.

When does it make sense to add class variables and methods? The best rule of thumb is to do so rarely, so that you don't overuse them. That said, it's a good idea to use class variables and methods:

  • To declare constants that any instance of the class can use (and whose value is fixed at development time)
  • On a class with utility methods that don't ever need an instance of the class (such as Logger.getLogger())

Class variables

To create a class variable, use the static keyword when you declare it:
accessSpecifier static variableName [= initialValue];
Note: The square brackets here indicate that their contents are optional. The brackets are not part of the declaration syntax.
The JRE creates space in memory to store each of a class's instance variables for every instance of that class. In contrast, the JRE creates only a single copy of each class variable, regardless of the number of instances. It does so the first time the class is loaded (that is, the first time it encounters the class in a program). All instances of the class share that single copy of the variable. That makes class variables a good choice for constants that all instances should be able to use.
For example, you declared the Gender attribute of Person to be a String, but you didn't put any constraints on it. Listing 8 shows a common use of class variables.
Listing 8. Using class variables
public class Person {
  //. . .
  public static final String GENDER_MALE = "MALE";
  public static final String GENDER_FEMALE = "FEMALE";
  // . . .
  public static void main(String[] args) {
  Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", GENDER_MALE);
    // . . .
  }
  //. . .
}

Declaring constants

Typically, constants are:
  • Named in all uppercase
  • Named as multiple words, separated by underscores
  • Declared final (so that their values cannot be modified)
  • Declared with a public access specifier (so that they can be accessed by other classes that need to reference their values by name)
In Listing 8, to use the constant for MALE in the Person constructor call, you would simply reference its name. To use a constant outside of the class, you would preface it with the name of the class where it was declared:
String genderValue = Person.GENDER_MALE;

Class methods

On the off chance that you've been taking after along since Part 1, you've as of now called the static Logger.getLogger() technique a few times — at whatever point you recovered a Logger example to compose yield to the support. See, however, that to do as such you didn't require an occurrence of Logger. Rather, you referenced the Logger class, which is the sentence structure for making a class technique call. Similarly as with class factors, the static catchphrase recognizes Logger (in this case) as a class strategy. Class strategies are additionally now and again called static techniques hence. 

Presently you can join what you found out about static factors and strategies to make a static technique on Employee. You proclaim a private static last factor to hold a Logger, which all occasions offer, and which is open by calling getLogger() on the Employee class. Posting 9 indicates how.
Listing 9. Creating a class (or static) method
public class Employee extends Person {
  private static final Logger logger = Logger.getLogger(Employee.class.getName());
  //. . .
  public static Logger getLogger() {
    return logger;
  }
}
Two important things are happening in Listing 9:
  • The Logger instance is declared with private access, so no class outside Employee can access the reference directly.
  • The Logger is initialized when the class is loaded — because you use the Java initializer syntax to give it a value.
To retrieve the Employee class's Logger object, you make the following call:
Logger employeeLogger = Employee.getLogger();
Previous Post
Next Post

post written by:

0 coment�rios: