1 / 61

Building Java Programs Chapter 15

Building Java Programs Chapter 15. Implementing a Collection Class: ArrayIntList. Exercise. Write a program that reads a file (of unknown size) full of integers and prints the integers in the reverse order to how they occurred in the file. Consider example file data.txt : 17 932085

rvega
Download Presentation

Building Java Programs Chapter 15

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Building Java ProgramsChapter 15 Implementing a Collection Class: ArrayIntList

  2. Exercise • Write a program that reads a file (of unknown size) full of integers and prints the integers in the reverse order to how they occurred in the file. Consider example file data.txt: 17 932085 -32053278 100 3 • When run with this file, your program's output would be: 3 100 -32053278 932085 17

  3. Solution using arrays int[] nums = new int[100]; // make a really big array int size = 0; Scanner input = new Scanner(new File("data.txt")); while (input.hasNextInt()) { nums[size] = input.nextInt(); // read each number size++; // into the array } for (int i = size - 1; i >= 0; i--) { System.out.println(nums[i]); // print reversed }

  4. Unfilled arrays int[] nums = new int[100]; int size = 0; • We often need to store an unknown number of values. • Arrays can be used for this, but we must count the values. • Only the values at indexes [0, size - 1] are relevant. • We are using an array to store a list of values. • What other operations might we want to run on lists of values?

  5. Other possible operations public static void add(int[] list, int size, int value, int index) public static void remove(int[] list, int size, int index) public static void find(int[] list, int size, int value) public static void print(int[] list, int size) ... • We could implement these operations as methods that accept a list array and its size along with other parameters. • But since the behavior and data are so closely related, it makes more sense to put them together into an object. • A list object can store an array of elements and a size, and can have methods for manipulating the list of elements. • Promotes abstraction (hides details of how the list works)

  6. Exercise • Let's write a class that implements a list using an int[] • We'll call it ArrayIntList • behavior: • add(value), add(index, value) • get(index), set(index, value) • size() • remove(index) • indexOf(value) • toString() ... • The list's size will be the number of elements added to it so far. • How will the list be used?...

  7. Implementing add • How do we add to the end of a list? • list.add(42);

  8. Implementing add, cont. • To add to end of list, just store element and increase size: public void add(int value) { list[size] = value; size++; } • list.add(42);

  9. Implementing add (2) • How do we add to the middle or end of the list? • list.add(3, 42); // insert 42 at index 3

  10. Implementing add (2) cont. • Adding to the middle or front is hard (see book ch 7.3) • must shift nearby elements to make room for the new value • list.add(3, 42); // insert 42 at index 3 • Note: The order in which you traverse the array matters!

  11. Implementing add (2) code public void add(int index, int value) { for (int i = size; i > index; i--) { list[i] = list[i - 1]; } list[index] = value; } • list.add(3, 42);

  12. Other methods • Let's implement the following methods next: • size • get • set • toString

  13. Implementing remove • How can we remove an element from the list? • list.remove(2); // delete 9 from index 2

  14. Implementing remove, cont. • Again, we need to shift elements in the array • this time, it's a left-shift • in what order should we process the elements? • what indexes should we process? • list.remove(2); // delete 9 from index 2

  15. Implementing remove code public void remove(int index) { for (int i = index; i < size; i++) { list[i] = list[i + 1]; } size--; list[size] = 0; // optional (why?) } • list.remove(2); // delete 9 from index 2

  16. Running out of space • What should we do if the client adds more than 10 elements? • list.add(15); // add an 11th element

  17. Convenience methods • Implement the following methods: • indexOf - returns the first index an element is found, or -1 if not • isEmpty - returns true if list has no elements • contains - returns true if the list contains the given int value • Why do we need isEmpty and contains when we already have indexOf and size ? • These methods provide convenience to the client of our class. if (myList.size() == 0) {if (myList.isEmpty()) { if (myList.indexOf(42) >= 0) {if (myList.contains(42)) {

  18. More ArrayIntList • Let's add some new features to our ArrayIntList class: 1. A method that allows client programs to print a list's elements 2. A constructor that accepts an initial capacity (By writing these we will recall some features of objects in Java.) • Printing lists: You may be tempted to write a print method: // client code ArrayIntList list = new ArrayIntList(); ... list.print(); • Why is this a bad idea? What would be better?

  19. The toString method • Tells Java how to convert an object into a String ArrayIntList list = new ArrayIntList(); System.out.println("list is " + list); // ("list is " + list.toString()); • Syntax: public String toString() { code that returns a suitable String; } • Every class has a toString, even if it isn't in your code. • The default is the class's name and a hex (base-16) number: ArrayIntList@9e8c34

  20. toString solution // Returns a String representation of the list. public String toString() { if (size == 0) { return "[]"; } else { String result = "[" + elementData[0]; for (int i = 1; i < size; i++) { result += ", " + elementData[i]; } result += "]"; return result; } }

  21. Multiple constructors • existing constructor: public ArrayIntList() { elementData = new int[10]; size = 0; } • Add a new constructor that accepts a capacity parameter: public ArrayIntList(int capacity) { elementData = new int[capacity]; size = 0; } • The constructors are very similar. Can we avoid redundancy?

  22. this keyword • this : A reference to the implicit parameter (the object on which a method/constructor is called) • Syntax: • To refer to a field: this.field • To call a method: this.method(parameters); • To call a constructor this(parameters); from another constructor:

  23. Revised constructors public ArrayIntList(int capacity) { elementData = new int[capacity]; size = 0; } public ArrayIntList() { this(10); // calls (int) constructor }

  24. Size vs. capacity • What happens if the client tries to access an element that is past the size but within the capacity (bounds) of the array? • Example: list.get(7); on a list of size 5 (capacity 10) • Answer: Currently the list allows this and returns 0. • Is this good or bad? What (if anything) should we do about it?

  25. Preconditions • precondition: Something your method assumes is trueat the start of its execution. • Often documented as a comment on the method's header: // Returns the element at the given index. // Precondition: 0 <= index < size public void remove(int index) { return elementData[index]; } • Stating a precondition doesn't really "solve" the problem, but it at least documents our decision and warns the client what not to do. • What if we want to actually enforce the precondition?

  26. Bad precondition test • What is wrong with the following way to handle violations? // Returns the element at the given index. // Precondition: 0 <= index < size public void remove(int index) { if (index < 0 || index >= size) { System.out.println("Bad index! " + index); return -1; } return elementData[index]; } • returning -1 is no better than returning 0 (could be a legal value) • println is not a very strong deterrent to the client (esp. GUI)

  27. Throwing exceptions (4.5) throw new ExceptionType(); throw new ExceptionType("message"); • Causes the program to immediately crash with an exception. • Common exception types: • ArithmeticException, ArrayIndexOutOfBoundsException, FileNotFoundException, IllegalArgumentException, IllegalStateException, IOException, NoSuchElementException, NullPointerException, RuntimeException, UnsupportedOperationException • Why would anyone ever want the program to crash?

  28. Exception example public void get(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException(index); } return elementData[index]; } • Exercise: Modify the rest of ArrayIntList to state preconditions and throw exceptions as appropriate.

  29. Postconditions • postcondition: Something your method promises will be trueat the end of its execution. • Often documented as a comment on the method's header: // Makes sure that this list's internal array is large // enough to store the given number of elements. // Postcondition: elementData.length >= capacity public void ensureCapacity(int capacity) { // double in size until large enough while (capacity > elementData.length) { elementData = Arrays.copyOf(elementData, 2 * elementData.length); } } • If your method states a postcondition, clients should be able to rely on that statement being true after they call the method.

  30. Writing testing programs • Some programs are written specifically to test other programs. • If we wrote ArrayIntList and want to give it to others, we must make sure it works adequately well first. • Write a client program with a main method that constructs several lists, adds elements to them, and calls the various other methods.

  31. Tips for testing • You cannot test every possible input, parameter value, etc. • Even a single (int) method has 2^32 different possible values! • So you must think of a limited set of tests likely to expose bugs. • Think about boundary cases • positive, zero, negative numbers • right at the edge of an array or collection's size • Think about empty cases and error cases • 0, -1, null; an empty list or array • an array or collection that contains null elements • Write helping methods in your test program to shorten it.

  32. More testing tips • Focus on expected vs. actual behavior • the test shouldn't just call methods and print results; it should: • call the method(s) • compare their results to a known correct expected value • if they are the same, report that the test "passed" • if they differ, report that the test "failed" along with the values • test behavior in combination • maybe add usually works, but fails after you call remove • what happens if I call add then size? remove then toString? • make multiple calls; maybe size fails the second time only

  33. Example ArrayIntList test public static void main(String[] args) { int[] a1 = {5, 2, 7, 8, 4}; int[] a2 = {2, 7, 42, 8}; int[] a3 = {7, 42, 42}; helper(a1, a2); helper(a2, a3); helper(new int[] {1, 2, 3, 4, 5}, new int[] {2, 3, 42, 4}); } public static void helper(int[] elements, int[] expected) { ArrayIntList list = new ArrayIntList(elements); for (int i = 0; i < elements.length; i++) { list.add(elements[i]; } list.remove(0); list.remove(list.size() - 1); list.add(2, 42); for (int i = 0; i < expected.length; i++) { if (list.get(i) != expected[i]) { System.out.println("fail; expect " + Arrays.toString(expected) + ", actual " + list); } } }

  34. Finishing ArrayIntList • Let's add the following features to ArrayIntList: • a constant for the default list capacity • better encapsulation and protection of implementation details • a better way to print list objects

  35. Class constants public static final typename = value; • class constant: a global, unchangeable value in a class • used to store and give names to important values used in code • documents an important value; easier to find and change later • classes will often store constants related to that type • Math.PI • Integer.MAX_VALUE, Integer.MIN_VALUE • Color.GREEN // default array length for new ArrayIntLists public static final int DEFAULT_CAPACITY = 10;

  36. "Helper" methods • Currently our list class has a few useful "helper" methods: • public void checkResize() • public void checkIndex(int index, int min, int max) • We wrote them to help us implement other required methods. • We don't want clients to call these methods; they are internal. • How can we stop clients from calling them?

  37. A private method privatetypename(typename, ..., typename) { statement(s); } • a private method can be seen/called only by its own class • encapsulated, similar to fields • your object can call the method on itself, but clients cannot call it • useful for "helper" methods that clients shouldn't directly touch private void checkIndex(int index, int min, int max) { if (index < min || index > max) { throw new IndexOutOfBoundsException(index); } }

  38. Printing an ArrayIntList • Currently our list class has a print method: // client code ArrayIntList list = new ArrayIntList(); ... list.print(); • Why is this a bad idea? What would be better?

  39. The toString method • Tells Java how to convert an object into a String ArrayIntList list = new ArrayIntList(); System.out.println("list is " + list); • Syntax: public String toString() { code that returns a suitable String; } • Every class has a toString, even if it isn't in your code. • The default is the class's name and a hex (base-16) number: ArrayIntList@9e8c34

  40. toString solution // Returns a String representation of the list. public String toString() { if (size == 0) { return "[]"; } else { String result = "[" + elementData[0]; for (int i = 1; i < size; i++) { result += ", " + elementData[i]; } result += "]"; return result; } }

  41. Exercise • Write a class called StutterIntList. • Its constructor accepts an integer stretch parameter. • Every time an integer is added, the list will actually add stretch number of copies of that integer. • Example usage: StutterIntList list = new StutterIntList(3); list.add(7); // [7, 7, 7] list.add(-1); // [7, 7, 7, -1, -1, -1] list.add(2, 5); // [7, 7, 5, 5, 5, 7, -1, -1, -1] list.remove(4); // [7, 7, 5, 5, 7, -1, -1, -1] System.out.println(list.getStretch()); // 3

  42. Inheritance • inheritance: Forming new classes based on existing ones. • a way to share/reuse code between two or more classes • superclass: Parent class being extended. • subclass: Child class that inherits behavior from superclass. • gets a copy of every field and method from superclass

  43. An Employee class public class Employee { ... public int getHours() { return 40; // works 40 hours / week } public double getSalary() { return 40000.0; // $40,000.00 / year } public int getVacationDays() { return 10; // 2 weeks' paid vacation } public String getVacationForm() { return "yellow"; // use the yellow form } } • Lawyers, Secretaries, etc. have similar behavior to the above. • How to implement those classes without redundancy?

  44. Inheritance syntax public class nameextends superclass { • Example: public class Lawyer extends Employee { ... } • By extending Employee, each Lawyer object now: • receives a copy of each method from Employee automatically • can be treated as an Employee by client code

  45. Overriding methods • override: To replace a superclass's method by writing a new version of that method in a subclass. • No special syntax is required to override a method.Just write a new version of it in the subclass. public class Lawyer extends Employee { // overrides getSalary method in Employee class; // give Lawyers a $5K raise public double getSalary() { return 45000.00; } }

  46. super keyword • Subclasses can call overridden methods with super super.method(parameters) • Example: public class Lawyer extends Employee { // give Lawyers a $5K raise (better) public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + 5000.00; } } • This version makes sure that Lawyers always make $5K more than Employees, even if the Employee's salary changes.

  47. Calling super constructor super(parameters); • Example: public class Lawyer extends Employee { public Lawyer(String name) { super(name);// calls Employee constructor } ... } • super allows a subclass constructor to call a superclass one. • The super call must be the first statement in the constructor. • Constructors are not inherited; If you extend a class, you must write all the constructors you want your subclass to have.

  48. Exercise solution public class StutterIntList extends ArrayIntList { private int stretch; public StutterIntList(int stretchFactor) { super(); stretch = stretchFactor; } public StutterIntList(int stretchFactor, int capacity) { super(capacity); stretch = stretchFactor; } public void add(int value) { for (int i = 1; i <= stretch; i++) { super.add(value); } } public void add(int index, int value) { for (int i = 1; i <= stretch; i++) { super.add(index, value); } } public int getStretch() { return stretch; } }

  49. Subclasses and fields public class Employee { private double salary; ... } public class Lawyer extends Employee { ... public void giveRaise(double amount) { salary += amount;// error; salary is private } } • Inherited private fields/methods cannot be directly accessed by subclasses. (The subclass has the field, but it can't touch it.) • How can we allow a subclass to access/modify these fields?

  50. Protected fields/methods protectedtypename; // field protectedtypename(typename, ..., typename) { statement(s); // method } • a protected field or method can be seen/called only by: • the class itself, and its subclasses • also by other classes in the same "package" (discussed later) • useful for allowing selective access to inner class implementation public class Employee { protected double salary; ... }

More Related