Title: oop
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

OOP - Object Oriented Programming Characteristics

Style Issues

Non-Modular Approach:


public class Company {
   private Vector employeeNames;
   private Vector vendorNames;

   public Company() {
      employeeNames = new Vector();
      vendorNames = new Vector();
   }

   public void addEmployee(String empName) {
      employeeNames.add(empName);
   }

   public void addVendor(String vendName) {
      vendorNames.add(vendName);
   }
}



In the above example, we are throwing everything into a single class. We have some methods related to Employees and some methods related to Vendors. If one person is assigned to work on changing our Employee code and another person is assigned to work on changing Vendor code, the 2 programmers would need to update the same class.

A More Modular Approach

Company.java


public class Company {
   private Employees employees;
   private Vendors vendors;

   public Company() {
      employees = new Employees();
      vendors = new Vendors();
   }

   public void addEmployeeName(String empName) {
      employees.addEmployeeName(empName);
   }
}


Employees.java


public class Employees {
   private Vector employeeNames;

   public Employees() {
      employeeNames = new Vector();
   }

   public void addEmployeeName(String empName) {
      employeeNames.add(empName);
   }
}

Vendors.java

public class Vendors {
   private Vector vendorNames;

   public Vendors() {
      vendorNames = new Vector();
   }

   public void addVendorName(String vendName) {
      vendorNames.add(vendName);
   }
}



Visibility Issues

It is considered a good style to provide setXXX methods for changing values in an object. However, a mistake is made in the following to leave the variables with the public modifier.


public class EmployeeInfo {
// It is usually not the best thing to allow variables to
// be public
   public String firstName;
   public String lastName;
   public int salary;

   public EmployeeInfo() {}




    //Define the interface (accessor) methods for EmployeeInfo
   public void setFirstName(String fName) {
      firstName = fName.trim(); //remove leading or trailing spaces
   }

   public void setLastName(String lName) {
      lastName = lName.trim(); //remove leading or trailing spaces
   }

   public void setSalary(int sal) {
      if (sal > 0) //canï¿½t have a negative salary
         salary = sal;
   }
}




Normally, you want other classes to access variables through setXXX methods. Even though we provided setXXX methods, leaving member variables public allows a client program to do something like the following:


public class Accessor {
   public static populateData(EmployeeInfo eInfo) {
      eInfo.firstName = ï¿½  Sarah ï¿½;
      eInfo.lastName = ï¿½  Grandeï¿½;
      eInfo.setSalary(-50000);
   }
}



The quick fix to the above problem is to change the public to private. In otherwords:


private String firstName;
private String lastName;
private int salary;


Interfaces: A More protected Approach

EmployeeInfo.java


public interface EmployeeInfo {
   public void setFirstName(String fName);
   public void setLastName(String lName);
   public void setSalary(int sal);
}



EmployeeInfoImpl.java


public class EmployeeInfoImpl implements EmployeeInfo {
   private String firstName;
   private String lastName;
   private int salary;

   private EmployeeInfoImpl() {}

   public static EmployeeInfo create() {
      return (EmployeeInfo)(new EmployeeInfoImpl());
   }

   //Define the interface (accessor) methods for EmployeeInfo
   public void setFirstName( String fName) {
      firstName = fName.trim(); //remove leading or trailing spaces
   }

   public void setLastName( String lName) {
      lastName = lName.trim(); //remove leading or trailing spaces
   }

   public void setSalary( int sal) {
      if (sal > 0) //canï¿½t have a negative salary
         salary = sal;
   }
}



Using EmployeeInfo


// Note that EmployeeInfo is actually an Interface, but it
// looks like a regular class.  This is a very common approach
// used by the more advanced features of Java.  XML, JDBC, Servlets
// extensively use this technique
EmployeeInfo info;

info = EmployeeInfoImpl.create();
info.setFirstName("Clem");
info.setLastName("Hasselbach");
info.setSalary(400000);


IS A Versus Has A

Ask yourself two questions when determining if the relationship between classes is ï¿½is aï¿½ or ï¿½has a.ï¿½ First ask: ï¿½Is the first class (for example, a circle) a type of the latter class (for example, a shape)?ï¿½ If so, then the relationship is ï¿½is a.ï¿½ You also may ask: ï¿½Does the first class (for example, a building) contain one or more of the second classï¿½ type (for example, rooms)?ï¿½ If so, then the relationship is ï¿½has a.ï¿½

What is the object-oriented relationship between a oak and an tree?
An is a relationship

What is the object-oriented relationship between a city and a road?
A has a relationship.

What is wrong with the following?


public class Building {
   long length;
   long width;

   public Building(long len, long wid) {
      length = len;
      width = wid;
   }

   public long area() {
      return length * width;
   }
}
// The following implies that an OfficeRoom is a Building ????

public class OfficeRoom extends Building {
   public OfficeRoom(long len, wid) {
      super(len, wid);
   }
}



Is this better?


public class Building {
   long length;
   long width;

   // The building has a number of office rooms:
   OfficeRoom [] officeRooms;

   public Building(long len, long wid, OfficeRoom [] ors) {
      length = len;
      width = wid;
      officeRooms = ors;
   }

   public long area() {
      return length * width;
   }
}
public class OfficeRoom {
   long length;
   long width;

   public OfficeRoom(long len, wid) {
   }

   public long area() {
      return length * width;
   }
}



Overloaded Methods:

Java allows multiple methods that have the same name. When 2 methods have the same name, the method is said to be overloaded.

Methods can be overloaded by different arguments, not by different return type. It is the number of arguments, the type of arguments, and the order of the type of arguments that matters. Where a method is public or private, synchronized, native, etc. is irrelavent in terms of overloaded methods.

It makes perfect sense for Java to only look at the arguments for determining which version of an overloaded method. This is because when someone codes a function call, the compiler can only count on seeing the arguments. For example it is optional to use the return code from a method.

It would be a good idea to go over the notes on Casting, because the prinicples covered in Casting come into play in choosing the appropriate method. When a method is called, you don't have to exactly match the data types of the parameters, but the following must be true:

The number of parameters passed must match the number of parameters declared in the method.

Must be possible to convert each actual parameter to required parameters datatype - For each parameter passed, it must be possible to convert the parameter to the type specified by the method. This should remind you of the discussion we had in the Casting notes where it was shown that its OK to assign a "larger" datatype equal to a "smaller" datatype. If you want to assign a "smaller" datatype equal to a "larger" datatype, you need to use a caste operation.

In Case of Ties - Because of overloading if 2 or more methods will satisfy the above rules, then the one "Closest" to the actual parameters is chosen. The decision of which is closest will be made at compile time.

Consider the following:


byte b = 1;
double d = 1.0;
void printd(double d)
{
}
void printb(byte b)
{
}

// legal calls:
printb(b);
printd(b);

// illegal call
printb(d);

// You can make it legal with a caste:
printb((byte)d);




If you have overloaded methods, then Java will pick what it thinks is the closest match. Execute the following and see if the results make sense:


class Test
{
 void print(double d)
 {
    System.out.println("print(double)");
 }
 void print(long l)
 {
    System.out.println("print(long)");
 }
 void print(short s)
 {
    System.out.println("print(short)");
 }

  public static void main(String[] args)
  {
    Test t = new Test();
    double d=1.0;
    float f=1.0f;
    long l=1;
    int i=1;
    short s=1;
    char c='a';
    byte b=1;

    System.out.print("double: ");
    t.print(d);

    System.out.print("float: ");
    t.print(f);

    System.out.print("long: ");
    t.print(l);

    System.out.print("int: ");
    t.print(i);

    System.out.print("short: ");
    t.print(s);

    // The following is a little surprising
    System.out.print("char: ");
    t.print(c);

    System.out.print("byte: ");
    t.print(b);


  }
}


For going from a "larger" to a "smaller" datatype, you need a caste, and the way you caste the parameter is used to select the appropriate overloaded method. Execute the following and see if it makes sense to you:


class Test2
{
 void print(short s)
 {
    System.out.println("print(short)");
 }

 void print(byte b)
 {
    System.out.println("print(byte)");
 }


  public static void main(String[] args)
  {
    Test2 t = new Test2();
    double d=1.0;
    float f=1.0f;
    long l=1;
    int i=1;
    short s=1;
    char c='a';
    byte b=1;

    System.out.print("(byte)double: ");
    t.print((byte)d);

    System.out.print("(short)float: ");
    t.print((short)f);

    System.out.print("(byte)long: ");
    t.print((byte)l);

// Note that even though there is a print that takes a short,
// the (byte) caste convinces java to use the print that takes a byte
    System.out.print("(byte)short: ");
    t.print((byte)s);

    System.out.print("short: ");
    t.print(s);

    System.out.print("(byte)char: ");
    t.print((byte)c);

    System.out.print("byte: ");
    t.print(b);


  }
}


Overloading when objects are passed as parameters:

Similar principles apply to methods that take objects as parameters:


class Rectangle
{
}
class Square extends Rectangle
{
}

class Test3
{
  void print(Square sq)
  {
    System.out.println("Square");
  }
  void print(Object o)
  {
    System.out.println("Object");
  }


  public static void main(String[] args)
  {
    Object ob_sq = new Square();
    Rectangle r_sq = new Square();
    Rectangle r = new Rectangle();
    Square s = new Square();
    Test3 t = new Test3();


    System.out.print("ob_sq - ");
    // we go to print(Square) because of caste
    t.print((Square)ob_sq);

    // print(Object) called because Rectangle is an Object,
    // and it doesn't pass as a Square
    System.out.print("r - ");
    t.print(r);

    System.out.print("r_sq - ");
    // we go to print(Object) because decision is made at compile
    // time. If the decision were made at run time, print(Square)
    // would be called
    t.print(r_sq);

    System.out.print("s - ");
    // Obviously this one should go to print(Square)
    t.print(s);

  }
}


The interesting surprise to me is that the decisions on which overloaded method called seems to be made at Compilation time instead of RunTime.

This is different than the discoveries we made earler regarding Polymorphism.

My guess is that choosing the overloaded method at Run time is just TOO EXPENSIVE.

Finding the best match -- what is the output?

In this example, the calling types match up exactly with appropriate methods


class Test {
   public static void sayIt(int a, long b) {
      System.out.println(" int - long");
   }

   public static void sayIt(int a, double b) {
      System.out.print(" int - double");
   }

   public static void sayIt(int a, int b) {
      System.out.print(" int - int ");
   }

   public static void main(String [] args) {
      sayIt(1,1);    // int - int
      sayIt(1,1.0);  // int - double
      sayIt(1,1L);   // int - long
   }
}



A little Tricker Best Match Problem -- what is the output?


public class Test4 {
   //first print method
   public void print(int a, long b, long c) {
      System.out.println("First");
   }

   //second print method
   public void print( long a, long b, long c) {
      System.out.println("Second");
   }
   public static void main(String[] args)
   {
     Test4 t = new Test4();

     // Should choose First because it's closest --- 2 parameters
     // need to be changed from int to long versus 3 for Second
     t.print(1,2,3); // int int int
   }
}



Change the second print method to look like:
public void print(long a, int b, long c){

Try compiling it. What is the problem -- in trying to find the "best" match, java has a tie. So java calls it ambiguous.

Change the call to:
t.print(1L, 2, 3);

Why does it now compile? We have made the first print method illegal without a caste. Consequently, the best and only choice is the second print routine.

Interesting point - Ambiguity of Overloaded methods is mostly an issue with the actual method call not the method declarations themselves.

Reasons for the this pointer

The this pointer points to the current obj




class Money {
     public double costs;
     public double revenue;
     public double profit;
     public Money (double costs) {
          this.costs = costs;  // Specifying a particular variable
          profit = Accounting.calculateProfit(this); // Passes itself
     }
}


Second Example:




class ThisSample{
     String screenTitle;
     public ThisSample() {
// When used the following statement must be the first statement in a constructor
          this(ï¿½Main Screenï¿½);// Calls the other constructor	
     }
     public ThisSample(String title) {
          screenTitle = title;
     }
}



Overloaded Constructors

Similar principles apply for overloaded Constructors as for methods.

this and super rules

1. Inside of a constructor you can have your first statement be either a this or super (not both).
2. The this pointer calls another constructor within your same class. Which constructor gets called is based on the arguments passed in the this(xx, yy) call using the overloading rules discussed earlier.
3. If the super(xx, yy) call is made, you are calling a constructor in your parent class.
4. If you have neither this or super, there is an implied call to:
super() - the default constructor(i.e. no parameter constructor) of your parent class. If your parent class has no default constructor then you get a compilation error.
5. If a class has no explicit constructors, then the compiler provides you a default constructor.
6. If you write one constructor, then the compiler does not provide any default constructor.

With the above rules in place, explain what is happening in the following:


public class MyClass {
   String title;
   int age=10;


   public MyClass() {
      this("Default Title");
      System.out.println("MyClass()");
   }

   public MyClass(String t) {
      this(t, 50);
      System.out.println("MyClass(String t)");
   }

   public MyClass(String t, int age)
   {
      title = t;
      this.age = age;
      System.out.println("MyClass(String t, int age)");
   }

   public static void main(String[] args)
   {
		// What output would be expected with each of the following.
        // What are the resultant values of title and age
		MyClass m = new MyClass();
        MyClass m2 = new MyClass("George");
        MyClass m3 = new MyClass("Fred", 55);
   }

}



What is happening in the following:


public class MySuper {
   //first constructor
   public MySuper() {
      System.out.println("Super1");
   }

   //second constructor
   public MySuper( String title) {
      System.out.println("Super2");
   }
}

public class MySub extends MySuper {
   //first constructor
   public MySub() {
      super("the title");
      System.out.println("Sub1");
   }

   //second constructor
   // Which constructor in MySuper will be called here?
   public MySub(String title) {
      System.out.println("Sub2");
   }
   public static void main(String[] args)
   {
   	MySub m1 = new MySub();
	MySub m2 = new MySub("abc");
   }
}



What is wrong with the following?? -- How do you fix it??:


public class A {
   public A( String title) {
      System.out.println("A");
   }
}

public class B extends A {
   public B( String title) {
      System.out.println("B");
   }
}



Last Updated: October 1, 2003 9:52 AM