Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
IOPROG – Ingmar Olsson Begin with Java 2 Begin with Java 2 Object-Oriented Programming Foundations Table of Contents Object-Oriented Programming – OOP ……………………………………… 2 Generics …………………………………………………………………………………… 23 Java and Database Technique without Database – ArrayList …. 38 An Application using ArrayList as a Database …………………………. 46 1 IOPROG – Ingmar Olsson Begin with Java 2 Object-Oriented Programming – OOP The three “pillows” In order to understand OOP, you need to understand the following three concepts or OOP “pillows”: Encapsulation Inheritance Polymorphism (“Begin with Java 2”) (“Begin with Java 3”) (“Begin with Java 3”) The first “pillow”: the concept class–encapsulation–abstract datatype In Java and all other OOP languages, we refer to an encapsulated abstract, textual or visual construction (template, blueprint ) as a class. A slogan: “Object Oriented Programmers do it with class!” What is a class? A BankAccount is a class, not the way the money is earned for the BankAccount. A Student is a class, not the air a student breath. As we already said on page 2 in “Begin with Java 1”, all Java programs consist of one or more class definitions. As we already said, even a stand-alone Java application needs a primary class definition, must have the same name as the filename it is saved in and requires a method named main(). What is an object? Other classes defined by a Java programmer (or available in the Java JDK via the import directive). Once the class definition is available, that programmer, (or other programmers), can use it to produce millions of nearly identical objects or instances (the terms object and instance are used interchangeably). An object is a software construct that encapsulates data, along with the ability to use or modify that data, into a software entity. What is an Object-Oriented Program? An Object-Oriented Program consists of a group of cooperating objects, exchanging messages, for the purpose of achieving a common objective. What is encapsulation? Encapsulation is the concept that an object should totally separate its interface (public methods in the class) from its implementation (the code in the methods). All the data and implementation code for an object should be entirely hidden behind its interface. The idea is that we can create an interface and, as long as that interface remains consistent, the application can interact with our objects. This remains true even if we entirely rewrite the code within a given method thus the interface is independent of the implementation. What is abstract datatype? Object oriented languages allow you to create whole new types with their own data (fields, data members) and with their own operations (methods) that apply to that internal data. They are called abstract data types. An abstract data type is a user-defined data type that satisfies the following two conditions: – The representation of, and operations on, objects of the type are defined in a single syntactic unit – The representation of objects of the type is hidden from the program units that use these objects, so the only operations possible are those provided in the type's definition An abstract data type is simply an encapsulation that includes only the data representation of one specific data type and the methods that provide the operations for that type An instance of an abstract data type is called an object. Object-oriented programming is an outgrowth of the use of data abstraction. Java’s support abstract data types. All user-defined data types in Java are classes and all objects are allocated from the heap and accessed through reference variables and the support for abstract data types in Java can only be defined in classes. Java also includes packages as one of its encapsulation constructs. 2 IOPROG – Ingmar Olsson Begin with Java 2 A visual approach: A class can be illustrated as a 3-compartment box A class can be visualized as a three-compartment box: 1. 2. 3. Name (or identity): identifies the class. Data/Variables (or attribute(s), state(s), field(s)): contains the static data of the class. Methods (or behaviour, function, operation): contains the dynamic behaviours of the class. This is in fact the visual notation that is used in UML, Unified Model Language. Name Data, Variables,Attributes Behavior Methods The followings figure shows a few examples of classes: Name, Identifier Variables, Attributes Student BankAccount balance id name deposit() withDraw() getName() printGrade() Methods, Behavior FootBallPlayer Circle name number radius color run() jump() getRadius() getArea() Car speed move() accelerate() The following figure shows two instances/objects of the class Student. b:BankAccount nisse:Student sven:Student balance = 50 id = 1234 name = “Nisse Hult” id = 5678 name = “Sven Kula” deposit() withDraw() getName() printGrade() getName() printGrade() The above class diagrams are drawn according to the UML (Unified Modeling Language) notations. A class is represented as a 3-compartment box, containing name, variables, and method. Class name can be shown in bold and centralized. Instance name is shown as instanceName:Classname and underlined. Summary In summary, a class is a programmer-defined, abstract, self-contained, reusable software entity that mimics a real-world thing. A class packages the identity (name), the static attributes (variables) and the dynamic behaviours (methods) in a 3-compartment box. An instance is an instantiation (or realization) of a particular item of a class. 3 IOPROG – Ingmar Olsson Begin with Java 2 A textual approach: a class can be given as a text in a textfile made by a texteditor. This texteditor must give a clean text encoded to UTF-8/ASCII without any embedded “rich” format. In Java, we use the keyword class to define a class and the textfile extension .java. For examples: //Filename BankAccount.java public class BankAccount { int balance; void deposit() {...} void withDraw() {...} //Class name //Variable, attribute //Methods } //Filename Circle.java public class Circle { double radius; String color; double getRadius() {...} double getArea() {...} //Class name //Variables, attributes //Methods } //Filename FootBallPlayer.java public class FootBallPlayer //Class name { int number; //Variables, attributes String name; int xLocation, yLocation; String run() {...} void kickBall() {...} //Methods } The syntax for class definition in Java is: <AccessControlModifier> class ClassName { //Class body contains definition of variables and methods ... } We shall explain the access control modifier, such as public and private, later. Class Naming Convention A class name shall be a noun, or a noun phrase made up of several words. All the words shall be initialcapitalized (camel-case). Use a singular noun for class name. Choose a meaningful and self-descriptive classname. For example, Circle, FotBallPlayer, HttpProxyServer, FileInputStream. Creating Instances of a Class To create an instance of a class, you have to: 1. 2. Declare an instance identifier (variable) of a particular class. Construct the instance (i.e., allocate storage for the instance and initialize the instance) using the "new" operator. Example OOP 1 Use the class BankAccount and place the text in the file BankAccount.java 4 IOPROG – Ingmar Olsson Begin with Java 2 The BackAccount class implements a deposit with 50 and a withdraw of 20, all handled by the variable balance. //Filename bankAccount.java public class BankAccount { private int balance; public void { balance = } public void { balance = } //Class name //Variable, attribute deposit() //Methods balance + 50; withDraw() balance – 20; } To create an instance/object of the class BankAccount we use the new operator BankAccount b = new BankAccount(); To make this happen we need the mandatory class with the static start method public static void main(String args[]) { } containing the instantiation of the BankAccount object. This class with the static start method must be given the same name as the textfile for this class. So if you give this class the name BankAccoutMain the textfile must be called BankAccountMain.java. It can be a good rule to give a name with the extension Main to this class with the star/main function. //Filename BankAccountMain.java public class BankAccountMain { public static void main(String args[]) { BankAccount b = new BankAccount(); } } In practice The practical work with this textual approach we need a text editor or with a convenient (Java) IDE. Text editor Select an optional folder/directory, for example ..\Native_Java\BankAccount (in Windows environment) or ../Native_Java/BankAccount (in Mac OS or Linux environment) and open a convenient texteditor: Emacs, WordPad, NotePad, . . . Step 1 Open those files in a texteditor with the filenames BankAccount.java and BankAccountMain.java //Filename BankAccount.java public class BankAccount //Class name 5 IOPROG – Ingmar Olsson Begin with Java 2 { int balance; //Variable, attribute void deposit() { balance = balance + 50; } void withDraw() { balance = balance – 20; } //Methods } //Filename BankAccountMain.java public class BankAccountMain { public static void main(String args[]) { BankAccount b = new BankAccount(); } } Now we need to open a CommandLine window (in Windows environment) or a Cygwin/Terminal/Shell (in Mac OS or Linux environment) to compile and execute the code. It is a good idea to have the texteditor and the CommandLine/Shell window in parallel windows on the same screen to easely toggle between them: editing <–> compile/execute for testing Compile and execute the code in the CommandLine/Terminal/Shell window. Both the compilation and the execution went well (we got no errors), but we application gave no functionality output. Step 1 is a “soft” starter to introduce the practice about using an texteditor and compile/execute for a standalone application. Then most of all we must apply the rules in object-oriented programming and increase the functionality. In the next step we investigate how the instantiation was possible by introducing the concept constructor. In fact we had no visible constructor in Step 1 so the Java compiler constructed one itself. Then we concentrate on access control and the important concept Information Hinding and Encapsulation We also introduce how to reference the variables and methods with the dot operator, . , and how to handle the read and write functionality, "getter" and "setter" Methods, from a class applying Information Hinding and Encapsulation 6 IOPROG – Ingmar Olsson Begin with Java 2 Dot Operator (. ) The variables and methods belonging to a class are formally called member variables and member methods. To reference a member variable or method, you must: 1. 2. first identify the instance you are interested in, and then reference the method or variable via the dot, . , operator. Constructors A constructor is a special method that has the same name as the class name. It is used to initialize the instance constructed. To create a new instance of a class, you need to use a special "new" operator followed by a call to (one of) the constructor(s). Although we often use the term constructor method, a constructor is different from an ordinary method is the following aspects: The name of the constructor method is the same as the class name, and begins with an uppercase. Constructor has no return type (or implicitly returns void). Hence, no return statement is allowed inside the constructor's body. Constructor can only be invoked via the "new" operator. It can only be used once to initialize the instance constructed. You cannot call the constructor afterwards. Constructors are not inherited (to be explained later). Access Control Modifiers: public vs. private An access control modifier can be used to control the visibility of a class, a member variable or a member method without a class. We begin with the following two access control modifiers: public: The class/variable/method is accessible and available to all the other objects in the system. private: The class/variable/method is accessible and available within this class only. if you don't place an access modifier on a method, class, or a variable, it is automatically recognized by the compiler as default. Default method can be accessed within the class where it was declared. In UML notations, public entity is denoted with a "+", while private entity is denoted with a "-" in the class diagram. More access control modifiers will be discussed later. Information Hiding and Encapsulation A class encapsulates the name, static attributes and dynamic behaviours into a "3-compartment box". Once a class is defined, you can seal up the "box" and put the "box" on the shelve for others to use and reuse. Anyone can pick up the "box" and use it in their application. This cannot be done in the traditional procedural language like C, as the static attributes (or variables) are scattered over the entire program and header files. You cannot "cut" out a portion of C program, plug into another program and expect the program to run without extensive changes. Member variables (attributes, fileds) of a class are typically hidden from the outside word (i.e., the other classes), with the private access control modifier. Access to the member variables are provided via the public assessor methods, e.g., getBalance(), getRadius() and setRadius(). This follows the principle of information hiding. That is, objects communicate with each others using well-defined interfaces. Objects are not allowed to know the implementation details of others. The implementation details are hidden or encapsulated within the class. Information hiding facilitates reuse of the class. Rule of Thumb: Do not make any variable public, unless you have a good reason. 7 IOPROG – Ingmar Olsson Begin with Java 2 "getter" and "setter" Methods To allow other classes to read the value of a private variable says xxx, you shall provide a get method (or getter or accessor method) called getXxx(). A get method need not expose the data in raw format. It can process the data and limit the view of the data others will see. get methods cannot modify the variable. To allow other classes to modify the value of a private variable says xxx, you shall provide a set method (or setter or mutator method) called setXxx(). A set method could provide data validation (such as range checking), and transform the raw data into the internal representation. Step 2 //Filename bankAccount.java public class BankAccount { private int balance; //Class name //Private variable, attribute BankAccount() { balance = 0; } //”Default” constructor void deposit() { balance = balance + 50; } void withDraw() { balance = balance – 20; } //Methods public int getBalance() { return balance; } //Get method, no set method yet } //Filename BankAccountMain.java public class BankAccountMain { public static void main(String args[]) { BankAccount b = new BankAccount(); b.deposit(); //Deposit 50 b.withDraw(); //Withdraw 20 System.out.println(b.getBalance()); //Output on the screen } } Step 3 Put comments on the constructor codelines, i.e. take this code away from the compilation and execution. //BankAccount() //{ // balance = 0; //] //”Default” constructor Try to deposit 50 and withdraw 20. Compile and execute the code. 8 IOPROG – Ingmar Olsson Begin with Java 2 Output: 30 The java compiler had created an implicit “default” constructor with the value balance = 0. Step 4 Take away the comments on the constructor codelines, i.e. compile and execute the “default” constructor. BankAccount() { balance = 0; ] //”Default” constructor Compile and execute the code. Output: 30 Do we need any “default” constructor? To construct robust and reliable code, YES! See, Step 6! Step 5 Initiate a new value balance = 100 in the “default” constructor. BankAccount() { balance = 100; ] //”Default” constructor Compile and execute the code. Output: 130 Method Overloading Method overloading means that the same method name can have a few different implementations. However, the different implementations must be distinguishable by their parameter/argument list (either the number of arguments, or the type of arguments, or their order). This rule about method overloading is valid for both ordinary methods and constructors. For example, the above BankAccount class has two (or more) versions of constructor differentiated by their argument list, as followed: BankAccount(); BankAccount(int amount ); //No parameter/argument, “default” constructor //One parameter/argument, “parameter” constructor Depending on the actual parameter/argument list used when invoking the method, the matching constructor will be invoked. If your argument list does not match any one of the methods, you will get a compilation error. Step 6 //Filename bankAccount.java public class BankAccount //Class name { private int balance; //Private variable, attribute //BankAccount() //{ // balance = 0; 9 IOPROG – Ingmar Olsson Begin with Java 2 //} BankAccount(int amount) //”Parameter” constructor { balance = balance + amount; } void deposit() //Methods { balance = balance + 50; } void withDraw() { balance = balance - 20; } int getBalance() { return balance; } } //Filename BankAccountMain.java public class BankAccountMain { public static void main(String args[]) { BankAccount b = new BankAccount(100); //Instansiering med “parameter” constructor b.deposit(); b.withDraw(); System.out.println("Output: " + b.getBalance()); } } Compile and execute the code. Output: 130 Do we need any “default” constructor? To construct robust and reliable code, YES! How can we be sure that the value is balance = 0?! Step 7 To get robust and reliable code, define also the “default” constructor by yourself! Take away the comments on the constructor codelines, i.e. compile and execute the “default” constructor. BankAccount() { balance = 0; } Compile and execute the code. Output: 130 10 IOPROG – Ingmar Olsson Begin with Java 2 Member Variables A member variable has a name (or identifier) and a type, as descried in the earlier chapter. Variable Naming Convention: A variable name shall be a noun, or a noun phrase made up of several words. The first word is in lowercase and the rest of the words are initial-capitalized, e.g., thefontSize, roomNumber, xMax, yMin and xTopLeft. Take note that variable name begins with an lowercase, while class name begins with an uppercase. The formal syntax for variable definition in Java is: <AccessControlModifier> type variableName <= initialValue>; <AccessControlModifier> type variableName-1 <= initialValue-1> <, type variableName-2 <= initialValue-2>> ... ; For example, private double radius; public int length = 1, width = 1; Member Methods A method : 1. 2. 3. receives parameters from the caller, performs the operations defined in the method body, and returns a piece of result (or void) to the caller. The syntax for method declaration in Java is as follows: <AccessControlModifier> returnType methodName (<argumentList>) { //Method body or implementation ...... } For examples: public int getBalance() { return balance; } public double getArea() { return radius*radius*Math.PI; } Method Naming Convention: A method name shall be a verb, or a verb phrase made up of several words. The first word is in lowercase and the rest of the words are initial-capitalized. For example, getRadius(), getParameterValues(). Take note that variable name is a noun (denoting a static attribute), while method name is a verb (denoting an action). They have the same naming convention. Nevertheless, you can easily distinguish them from the context. Methods take arguments in parentheses (possibly zero argument with empty parentheses), but variables do not. In this writing, methods are denoted with a pair of parentheses, e.g., println(), getArea() for clarity. 11 IOPROG – Ingmar Olsson Begin with Java 2 IDE proposal: BlueJ Using BlueJ Start BlueJ and the BlueJ interface opens. Select the menu New Project. Choose and invoke a project name, for example BankAccount and select a relative location in the directory/folder system, for example BlueJ_JavaProjects A new BlueJ window is opened. The document icon is a README file editor and is an important feature to be filled in for all applications. This can be opened and updated at an optional occasion and we leave it for now. Select and click New Class … button. Fill in the name of class, in this case BankAccount 12 IOPROG – Ingmar Olsson Begin with Java 2 A striped UML class diagram alike graphical notation is created. Double-click on the class diagram icon. A texteditor is opened. A textfile is proposed with a private variable/attribute/field, a “default” constructor and a public method. Delete this code! Fill in the code for BankAccount.java used in Example OOP 1, Step 2. Save the file. Navigate to the selected directory/folder for the BlueJ project and open the projrct directory/folder BankAccount. There is the file BankAccount.java textfile, exactly the same as created in the stand-alone scenario we made earlier. 13 IOPROG – Ingmar Olsson Begin with Java 2 Now go back to the “New Class …” window and create a new class BankAccountMain Now we have two “striped” Class diagram icons, BankAccout and BankAccountMain. We also got the relation between the two classes (the instantiation of the BankAccount clas in the BankAccountMain class) as a IML “uses” relation notated as arrow between the classes. Click the Compile button. The stripes disappear (when the compilation is succeeded) and a message Compiling .- Done is written in the lower frame of the window. Navigate to the selected directory/folder for the BlueJ project and open the project directory/folder BankAccount. Now there are all the files BankAccount.java, BankAccountMain.java, BankAccount.class and BankAccountMain.class. 14 IOPROG – Ingmar Olsson Begin with Java 2 How to instantiate classes and run the application from within BlueJ. Right-click or ctrl-click on the BankAccount class diagram. The a pop-up windo with the text new BankAccont() appears. Activate the new BankAccount() alternative and a new pop-up window opens. Invoke an optional name for the BankAccount instance, for example (as in Example OOP 1) b. Click OK and a red icon for the instantiated object b is visible in the lower area called object bench. The opened pop-up window lshow the methods in the instantiation b. There is also an alternative called inspect. Select this option and a pop-up for the data (variables, attributes,fields) in the object b is open. In the same way we can instatiate an object of BankAccountMain class and call it for example bMain. But there are no accessible methods because the only methotd we have in BankAccountMain is the static void main(String args[]) mrethod and this is not accessible through an object, only direct as a starting method for all Java applications. 15 IOPROG – Ingmar Olsson Begin with Java 2 Right-click or ctrl-click on the BankAccountMain class diagram. The a pop-up windo with the option void main(String[] args) appears. Then a pop-up window with the call/referens to BanAccountMain.main appears. For the moment we give no argument, just click OK to run the main() method. Then BlueJ opens a terminal window with the (expected) outout 30. This is comparable to run the Java virtual machine with the java command in a stand-alone application. 16 IOPROG – Ingmar Olsson Begin with Java 2 Using the BlueJ Debugger The main tasks for a debugger are setting breakpoints stepping through the code inspecting variables This can be used to find errors but also to investigate the flow when the code in an application is executed. BlueJ has a debugger integrated in the environment. Open the code for the BankAccount class. Click on the left margin and put a breakpoint on the first line in the parameter constructor BankAccount(int amount) Run the BankAccount application from the BankAccountMain class. Running the application from the BankAccountMain class wiil show a black arrow in the left marginal following the executed row. A debug window is shown. If a breakpoint is set in BankAccountMain on the constructor instantiation, clicking the Step tool in the Debugger window can be used to follow the execution within the constructor BankAccountMain(int amount) in the BankAccount class. The static, instance and local variables can automatically be inspected in the debug window. 17 IOPROG – Ingmar Olsson Begin with Java 2 IDE proposal: Eclipse Using Eclipse Create a new Java project with the name BankAccount. Clck Finish. The projekt is opened in the Project Explorer Open (right-click or ctrl-click) the src option and select New Class option. Choose for moment not to fill in the Package field (default package). Invoke the class name BankAccount. Don’t check the “public static void main(String[] args)” checkbox. Click Finish. 18 IOPROG – Ingmar Olsson Begin with Java 2 You get an empty class BackAccount with the filename BankAccount.java in the texteditor. Fill in the code in BankAcoount class we have used earlier. Create a new class BankAccountMain (in the same default package). Check the “public static void main(String[] args)” checkbox. Click Finish. 19 IOPROG – Ingmar Olsson Begin with Java 2 The BankAccountMain class with the public static void main(String[] args) function is filled in. Fill in the code in BankAcoountMain class we have used earlier. Open the Run menu. WE get the expected output in the Console view. 20 IOPROG – Ingmar Olsson Begin with Java 2 Using the Eclipse Debugger ..... Example OOP 2 Constructors - again In a class Circle, there are three so-called constructors Circle(...). Circle c1 = new Circle(); Circle c2 = new Circle(2.0); Circle c3 = new Circle(3.0, "Red"); For examples, suppose that we have a class called Circle, we can create instances of Circle as follows: // Declare 3 instances of the class Circle, c1, c2, and c3 Circle c1, c2, c3; // Allocate and construct the instances via new operator c1 = new Circle(); c2 = new Circle(2.0); c3 = new Circle(3.0, "Red"); // You can declare and construct in the same statement Circle c4 = new Circle(); Although we often use the term constructor method, a constructor is different from an ordinary method is the following aspects: The name of the constructor method is the same as the class name, and begins with an uppercase. Constructor has no return type (or implicitly returns void). Hence, no return statement is allowed inside the constructor's body. Constructor can only be invoked via the "new" operator. It can only be used once to initialize the instance constructed. You cannot call the constructor afterwards. Constructors are not inherited (to be explained later). Dot Operator (. ) The variables and methods belonging to a class are formally called member variables and member methods. To reference a member variable or method, you must: 3. 4. first identify the instance you are interested in, and then reference the method or variable via the dot, . , operator. For example, suppose that we have a class called Circle, with two variables (radius and color) and two methods (getRadius() and getArea()). We have created three instances of the class Circle, namely, c1, c2 and c3. To invoke the method getArea(), you must first identity the instance of interest, says c2, then use the dot operator, in the form of c2.getArea(), to invoke the getArea() method of instance c2. For example, // Declare and construct instances c1 and c2 of the class Circle Circle c1 = new Circle (); Circle c2 = new Circle (); // Invoke member methods for the instance c1 via dot operator System.out.println(c1.getArea()); System.out.println(c1.getRadius()); // Reference member variables for instance c2 via dot operator c2.radius = 5.0; //Notice the “Rule of Thumb” on page 9! c2.color = "Blue"; //Notice the “Rule of Thumb” on page 9! 21 IOPROG – Ingmar Olsson Begin with Java 2 In general, suppose there is a class called AClass with a member variable called aVariable and a member method called aMethod(). An instance called anInstance is constructed for AClass. You use anInstance.aVariable and anInstance.aMethod(). Now we put this together in a practical session. A class called Circle is to be defined with two variables: radius of type double and color of type String; and three methods: getRadius(), getColor(), and getArea(), as illustrated in the following class diagram: Class defintion Circle -radius:double -color:String +getRadius():double +getArea():double +getColor():String Instances c1:Circle c2:Circle c3:Circle -radius = 2.0 -color = “Blue” -radius = 2.0 -color = “Red” -radius = 1.0 -color = “Red” +getRadius() +getArea() +getColor() +getRadius() +getArea() +getColor() +getRadius() +getArea() +getColor() The source codes for Circle.java is as follows: 22 IOPROG – Ingmar Olsson Begin with Java 2 public class Circle { //Private variables private double radius; private String color; //Constructors (overloaded) public Circle() { radius = 1.0; color = "Red"; } public Circle(double r) { radius = r; color = "Red"; } public Circle(double r, String c) { radius = r; color = c; } // Circle class, saved as "Circle.java" // 1st Constructor // 2nd Constructor // 3rd Constructor //Public methods public double getRadius() { return radius; } public String getColor() { return color; } public double getArea() { return radius*radius*Math.PI; } } Compile Circle.java. Notice that the Circle class does not have a main() method. Hence, it is not a standalone program and you cannot run the Circle class by itself. Circle class is meant to be used in other classes. We shall now write another class called CircleMain, which uses the Circle class. TestCircle has a main() method and can be executed. public class CircleMain { public static void main(String[] args) { //Construct an instance c1 of the class Circle Circle c1 = new Circle(2.0, "blue"); //Uses 3rd constructor System.out.println("Radius is " + c1.getRadius() //Use dot operator //to invoke member methods + " Color is " + c1.getColor() + " Area is " + c1.getArea()); //Construct an instance c2 of the class Circle Circle c2 = new Circle(2.0); //Uses 2nd constructor System.out.println("Radius is " + c2.getRadius() + " Color is " + c2.getColor() + " Area is " + c2.getArea()); //Construct an instance c3 of the class Circle Circle c3 = new Circle(); // uses 1st constructor System.out.println("Radius is " + c3.getRadius() + " Color is " + c3.getColor() + " Area is " + c3.getArea()); } } 23 IOPROG – Ingmar Olsson Begin with Java 2 Compile and run the CircleMain and study the outputs: Radius is 2.0 Color is blue Area is 12.566370614359172 Radius is 2.0 Color is red Area is 12.566370614359172 Radius is 1.0 Color is red Area is 3.141592653589793 In our Circle class, the variables radius and color are declared private. That is to say, they are only available within the Circle class and not visible in any other classes - including CircleMain class. You cannot access the private variables radius and color from the CircleMain class directly - via says c1.radius or c1.color. The Circle class provides two public accessor methods, namely, getRadius() and getColor(). These methods are declared public. The class CircleMain can invoke these public accessor methods to retrieve the radius and color of a Circle object, via says c1.getRadius() and c1.getColor(). There is no way you can change the radius or color of a Circle object, after it is constructed in the CircleMain class. You cannot issue statements such as c1.radius = 5.0 to change the radius of instance c1, as radius is declared as private in the Circle class and is not visible to other classes including CircleMain. If the designer of the Circle class permits the change the radius and color after a Circle object is constructed, he/she has to provide the appropriate set methods, e.g., // Setter for color public void setColor(String c) { color = c; } // Setter for radius public void setRadius(double r) { radius = r; } With proper implementation of information hiding, the designer of a class has full control of what the user of the class can and cannot do. Keyword "this" You can use keyword "this" to refer to this class inside a class definition. One of the main usage of keyword this is to resolve ambiguity. public class Circle { double radius; public Circle(double radius) { this.radius = radius; } ... } //Member variable called radius //Method's argument also called radius In the above codes, there are two identifiers called radius - a member variable of the class and the method's argument. This certainly causes naming conflict. To avoid the naming conflict, you could name the method's argument r instead of radius. However, radius is certainly more approximate and meaningful in this context. Java provides a keyword called this to resolve this naming conflict. "this.radius" refers to the member variable; while "radius" refers to the method's argument. Using the keyword "this", the getter and setter methods for a private variable called xxx of type Ttt are as follows: 24 IOPROG – Ingmar Olsson Begin with Java 2 // A variable named xxx of type Ttt Ttt xxx; // A getter for variable xxx of type Ttt receives no argument and return a value of type Ttt Ttt getXxx() { return xxx; } // A setter for variable xxx of type Ttt receives a parameter of type Ttt and return void void setXxx(Ttt xxx) { this.xxx = xxx; } Furthermore: In a constructor, we can use this(...) to call another constructor. Inside a method, we can use the statement "return this" to return this instance to the caller. this.varName refers to varName of this class. this.methodName(...) invokes methodName(...) of this class. Method toString() Every well-designed Java class should have a public method called toString() that returns a string description of the object. You can invoke the toString() method explicitly by calling instanceName.toString() or implicitly via println() or String concatenation operator '+'. Running println(anInstance) with an object argument invokes the toString() method of that instance implicitly. For example, if the following toString() method is included in our Circle class: public String toString() { return "Circle with radius = " + radius + " and color of " + color; } In the CircleMain class, you can get a short text descriptor of a Circle object by: Circle c1 = new Circle(); //Explicit call System.out.println(c1.toString()); //Implicit call to c1.toString() System.out.println(c1); System.out.println("c1 is: " + c1); The signature of toString() is: public String toString() { ...... } Class Variables, Class Methods and Constants When a number of objects are created from the same class blueprint, they each have their own distinct copies of instance variables. In the Circle class (Example OOP 1) the instance variable are radius and color. Each Circle object has its own values for these variables, stored in different memory locations. Sometimes, there is a need for variables that are common to all objects. This is accomplished with the static modifier. Fields that have the static modifier in their declaration are called static fields or class variables. They are associated with the class, rather than with any object. Every instance of the class shares a class variable, which is in one fixed location in memory. Any object can change the value of a class variable, but class variables can also be manipulated without creating an instance of the class. A typical use of a class variable is to count the number of instances you have created with the constructors. In the Circle class you can use //Add a class variable for the number of Circle objects instantiated private static int numberOfCircles = 0; 25 IOPROG – Ingmar Olsson Begin with Java 2 Class variables are referenced by the class name itself, as in Circle.numberOfCircles You can use the Circle constructor to set the id instance variable and increment the numberOfCircles class variable with numberOfCircles++; in all constructors. If you want to printout the number of instances with the call Circle.numberOfCircles you must use the public access modifier public static int numberOfCircles = 0; This breaks the class encapsulation idea! A better way is to use a static method! The Java programming language supports static methods as well as static variables. Static methods, which have the static modifier in their declarations, should be invoked with the class name, without the need for creating an instance of the class, as in ClassName.methodName(args) A common use for static methods is to access static fields. For example, we could add a static method to the Circle class to access the numberOfCircles static field: public static int getNumberOfCircles() { return numberOfCircles; } This method can then be used e.g. in a printout statements as System.out.println("Number of Circle instances: " + Circle.getNumberOfCircles()); The static modifier, in combination with the final modifier, is also used to define constants. The final modifier indicates that the value of this field cannot change. For example, the following variable declaration defines a constant named PI, whose value is an approximation of pi (the ratio of the circumference of a circle to its diameter): static final double PI = 3.141592653589793; Constants defined in this way cannot be reassigned. By convention, the names of constant values are spelled in uppercase letters. If the name is composed of more than one word, the words are separated by an underscore (_). Example OOP 3 This application is a cash register that totals up sales and computes change due. Using a texteditor IDE Implement a CashRegister class. Use an optional texteditor and create a file with the filename CashRegister.java. Implement a “default” constructor (constructor without input parameters) and the mutator methods recordPurchase(), enterPayment() and the accessor method getChange(). The mutator method, sometimes called a "setter", is most often used in object-oriented programming, in keeping with the principle of encapsulation. According to this principle, member variables of a class are made private to hide and protect them from other code, and can only be modified by a public member function (the mutator method), which takes the desired new value as a parameter, optionally validates it, and modifies the private member variable. Often a "setter" is accompanied by a "getter" (also known as an accessor), which returns the value of the private member variable. //CashRegister.java /** A cash register totals up sales and computes change due. */ public class CashRegister { private double purchase; 26 IOPROG – Ingmar Olsson Begin with Java 2 private double payment; /** Constructs a cash register with no money in it. */ public CashRegister() { purchase = 0; payment = 0; } /** Records the sale of an item. @param amount the price of the item */ public void recordPurchase(double amount) { double total = purchase + amount; purchase = total; } /** Enters the payment received from the customer. @param amount the amount of the payment */ public void enterPayment(double amount) { payment = amount; } /** Computes the change due and resets the machine for the next customer. @return the change due to the customer */ public double getChange() { double change = payment - purchase; purchase = 0; payment = 0; return change; } } Implement the class CashRegisterMain in the file CashRegisterMain.java //CashRegisterMain.java import java.util.Scanner; /** A class to start the CashRegister class. */ public class CashRegisterMain { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Price of item: "); double purchase1 = sc.nextDouble(); System.out.print("Price of item: "); double purchase2 = sc.nextDouble(); System.out.print("Payment: "); double payment = sc.nextDouble(); CashRegister register = new CashRegister(); 27 IOPROG – Ingmar Olsson Begin with Java 2 register.recordPurchase(purchase1); register.recordPurchase(purchase2); register.enterPayment(payment); double change = register.getChange(); System.out.println("Amount in return: " + change); } } Compile and run (in an optional Shell tool or Command Prompt) the CashRegister application from the folder with the files CashRegisterMain.java and CashRegister.java. Using BlueJ IDE Create a new class CashRegister with the same code as in CashRegister.java above. Create a new class CashRegisterMain with the same code as in CashRegisterMain.java above. Run the application from the BlueJ IDE and activate the void main(String[] args) method from the class CashRegisterMain. 28 IOPROG – Ingmar Olsson Begin with Java 2 Interact with the application from the opened BlueJ terminal window. Another way to execute the application directly in BlueJ is to right-click the class CashRegister and create a new instance of the class without starting from the main() method. From the opened BlueJ Method Call window for the CashRegister instance, the methods void recordPurchase(double amount); void enterPayment(double amount); double getChange() can be called in the same way as from the main() method. 29 IOPROG – Ingmar Olsson Begin with Java 2 Exercise OOP 1 Use the keyword this to get the correct output from the instantiation of the class Account. public class Exercise1 { private int a; private int b; public Exercise1() {} public void setData(int a ,int b) { a = a; b = b; } public void showData() { System.out.println("Value of A = "+ a); System.out.println("Value of B = "+ b); } public static void main(String args[]) { Exercise1 e = new Exercise1(); 30 IOPROG – Ingmar Olsson Begin with Java 2 e.setData(2,3); e.showData(); } } Exercise OOP 2 Public class Exercise2 { private int a; private int b; public Exercise2() {} public void setData(int c,int d) { a=c; b=d; } public void showData() { System.out.println("Value of a = "+a); System.out.println("Value of a = "+b); } public static void main(String args[]) { Exercise2 e1 = new Exercise2(); Exercise2 e2 = new Exercise2(); e1.setData(1,2); e2.setData(3,4); e1.showData(); e2.showData(); //Exercise2 e3; //e3=e2; //e3.showData(); //e2=null; //e3.showData(); //e3=null; //e3.showData(); //1) //1) //1) //2) //2) //3) //3) } } Uncomment the lines numbered 1). Save , compile and run the code. Two reference variables are pointing to the same object. Describe in a figure the heap Uncomment the lines numbered 2). Save, compile and run the code. Describe in a heaps the reference variables. s2 is eligible for garbage collection but s3 is not. Why? Uncomment the lines numbered 3) . Save , compile and run the code. Which references are eligible for garbage collection? Exercise OOP 3 public class Exercise3 { private int a; //Initialized to zero private static int b; //Initialized to zero only when class is loaded not for each object //created. public Exercise3() { b++; } //Constructor incrementing static variable b public void showData() { System.out.println("Value of a = "+a); 31 IOPROG – Ingmar Olsson Begin with Java 2 System.out.println("Value of b = "+b); } //public static void increment() //{ //a++; //} } class Main { public static void main(String args[]) { Exercise3 e1 = new Exercise3(); e1.showData(); Exercise3 e2 = new Exercise3(); e2.showData(); //Exercise3.b++; //1) //e1.showData(); //1) } } Uncomment the line numbered 1). Save , compile and run the code. Describe how reference variables and objects are created and static variables are accessed by the different instances. What kind of class in Main? Exercise OOP 4 public class Book { private int itemCode; private String title; public int getItemCode() { return itemCode; } public void setItemCode (int newItemCode) { if (newItemCode > 0) itemCode = newItemCode; } public String getTitle() { return title; } public void setTitle (String newTitle) { title = newTitle; } public void display() { System.out.println(itemCode + " " + title); } } public class BookMain { public static void main(String[] args) { Book b = new Book(); b.setItemCode( . . . . ); b.setTitle(". . . . .”); System.out.println(b.getItemCode() + " " + b.getTitle()); System.out.print("From display: "); b.display(); } } Use the class BookMain to instantiate an object of a Book. Exercise OOP 5 32 IOPROG – Ingmar Olsson Begin with Java 2 The following information will be stored for each employee: employee ID (an integral number, automatically assigned when a new employee is added) first name and last name department number (an integral value, call it dept) pay rate (a floating-point value, using a double) In addition, we would like: a method called getPayInfo() that will return a sentence with the employee's name, id, department, and check amount a method called getFullName() that will return the first and last names separated by a space Define a class called Employee with these characteristics, using standard practices for limiting data access and for method naming In order to be useful, there should be methods to set and get all properties (except setting the employee id, which will happen automatically in a manner to be determined later; for now, just let it default to 0) Create a class called Payroll with a main method, it should: instantiate an Employee object set values for all properties call the getPayInfo() method to see the results Modify the Employee class to use a constructor that accepts parameters for the first and last names, department, and pay rate. In Payroll, modify main to add another Employee variable that receives an instance created using this constructor . Also add a constructor that takes no parameters and does nothing (as we discussed above) Add more Employee constructors: one that takes values for first and last names only one that takes values for first name, last name, and department one that takes values for first name, last name, and pay rate note that, in practice, you can use the parameter lists for constructors to help enforce what you would consider valid combinations of properties - for example, if you would not want an employee to exist in a state where they had name and department information, but no pay rate, then the absence of that particular constructor would help ensure that judicious use of copy-paste-edit can speed up this process, but be careful to make every necessary edit if you do this! Create and pay additional instances using these constructors You will find yourself writing somewhat repetitive code, setting the same values the same way in several different constructors - we will address this in a few pages Add a private and static integer property called nextId to the Employee class (give it an initial value of 1). Modify the declaration of the id property as follows: private int id = nextId++; What happens when each new Employee gets instantiated? Exercise OOP 6 Implement an object-oriented class version BubbleSort of the earlier prepared example (“Begin with Java 1”) with the Bubblesort algoritm. Implement a constructor BubbleSort() with the same code for generating the random numbers as the void slumptal(int a[], int n) method. Give the BubbleSort class the private datamembers private int a[]; private int n; Change the classification for the other methods to class member methods (delete the word static). Use the below proposal as a main() method. public static void main(String args[]) { System.out.print("Give the number of integers to be sorted: "); Scanner input = new Scanner(System.in); int antal; antal = input.nextInt(); 33 IOPROG – Ingmar Olsson Begin with Java 2 int helTal[] = new int[antal]; BubbleSort bSort = new BubbleSort(helTal, antal); System.out.println("Unsorted: "); bSort.skrivut(helTal, antal); bSort.sortering(helTal, antal); System.out.println("Sorted: "); bSort.skrivut(helTal,antal); } Exercise OOP 7 Implement an object-oriented class version MasterMind of the earlier prepared example of the MasterMind game. Implement the datastructure in the earlier code as private datamembers in the class MasterMind. Implement a default constructor MasterMind() to call the same code as in earlier method play(). Change the classification for the other static methods to class member methods. Use the below proposal as a main() method. public static void main(String[] args) { Scanner scan = new Scanner(System.in); char c; do { MasterMind mastermind = new MasterMind(); System.out.print("Once more? (Y/N): "); String s = scan.next(); c = s.charAt(0); } while (c == 'y' || c == 'Y'); } Exercise OOP 8 Implement a TicketMachine class for a ticket machine that issues fare tickets. Use for example the private datamembers // The price of a ticket from this machine. private int price; // The amount of money entered by a customer so far. private int balance; // The total amount of money collected by this machine. private int total; and the constructor TicketMachine(int ticketPrice) to initiate the datamembers Use methods like int getPrice(), getting the ticket price specified in the constructor void insertMoney(int amount), control enough money for the ticket void printTicket(), print a simple textual layout Implement a control that a ticket will only be printed if enough money has been input. If using a texteditor/shell tool IDE instantiate the class TicketMachine from a class TicketMachineMain with a static main() method. If you are using BlueJ IDE you can instantiate and call the methods directly from the BlueJ IDE. Exercise OOP 9 Playing cards. In a typical card game, each player gets a hand of cards. The deck is shuffled and cards are dealt one at a time from the deck and added to the players' hands. In some games, cards can be removed from a hand, and new cards can be added. The game is won or lost depending on the value (ace, 2, ..., king) and suit (spades, diamonds, clubs, hearts) of the cards that a player receives. If we look for nouns in this description, there are several candidates for objects: game, player, hand, card, deck, value, and suit. Of these, the value and the suit of a card are simple values, and they will just be represented as instance variables in a Card object. The ones that are most obviously reusable are: card, hand, and deck. 34 IOPROG – Ingmar Olsson Begin with Java 2 If we look for verbs in the description of a card game, we see that we can shuffle a deck and deal a card from a deck. This gives use us two candidates for instance methods in a Deck class. Cards can be added to and removed from hands. This gives two candidates for instance methods in a Hand class. Cards, the Card class, are relatively passive things, but we need to be able to determine their suits and values. A program that lets the user play a very simple card game is the card version of HighLow. A deck of cards is shuffled, and one card is dealt from the deck and shown to the user. The user predicts whether the next card from the deck will be higher or lower than the current card. If the user predicts correctly, then the next card from the deck becomes the current card, and the user makes another prediction. This continues until the user makes an incorrect prediction. The number of correct predictions is the user's score. The following step of this card game is BackJack which will be covered later on. Implement a program HighLow that uses the Card and Deck classes. The files Card.java and Deck.java are available as separate files and discussed separately. public class HighLow { public static void main(String[] args) { //Output som instructions for the HighLow cardgame // Proposals for optional datamembers int gamesPlayed = 0; // Number of games user has played. int sumOfScores = 0; // The sum of all the scores from all the games played. char playAgain; // User's response for another game. do { int someResult; someResult = play(); // Play the game and get the result (score). // . . . . . // Output "Play again? Y/N"; // . . . . . } while (playAgain == ‘y’ || playAgain == ‘Y’); // Output of the result of the game(s) } static int play() { Deck deck = new Deck(); // Get // the Card currentCard; // The Card nextCard; // The char guess; // The user's a new deck of cards, and store a reference to it in variable deck current card. next card in the deck. guess. 'H' for higher, 'L' for lower. deck.shuffle(); currentCard = deck.dealCard(); //Output currentCard while (true) { // Get the user's prediction, 'H' or 'L'. // . . . . . do { //. . . . . } while (guess != 'H' && guess != 'L'); //Get the next card and show it to the user. nextCard = deck.dealCard(); //Output nextCard //Check the user's prediction. */ if (nextCard.getValue() == currentCard.getValue()) { //. . . . . break; } else if (nextCard.getValue() > currentCard.getValue()) 35 IOPROG – Ingmar Olsson Begin with Java 2 { if (guess == 'H') { //. . . . . } else { //. . . . . break; } } else { if (guess == 'L') { //. . . . . } else { //. . . . . break; } } //Next iteration of the loop, the nextCard becomes the currentCard //Output the currentCard } //Output some result about the guesses return someResult; } } Generics 36 IOPROG – Ingmar Olsson Begin with Java 2 Introduction Generics are a very important change to the Java language in a long time, possibly since it was created. They make reusable Java code easier to write and read. It's been more than 10 years since the introduction of the Java programming language. In that time, the Java language has matured and come into its own. But only with Java 5.0, the sixth major release of Java, did the core language itself change in a significant way. Generics are about abstraction. Generics let you create classes and methods that work in the same way on different types of objects. The term "generic" comes from the idea that we'd like to be able to write general algorithms that can be broadly reused for many types of objects rather than having to adapt our code to fit each circumstance. This concept is not new; it is the impetus behind object-oriented programming itself. Java generics do not so much add new capabilities to the language as they make reusable Java code easier to write and easier to read. Generics take reuse to the next level by making the type of the objects we work with an explicit parameter of the generic code. For this reason, generics are also referred to as parameterized types. In the case of a generic class, the developer specifies a type as a parameter (an argument) whenever she uses the generic type. The class is parameterized by the supplied type, to which the code adapts itself. In other languages, generics are sometimes referred to as templates, which is more of an implementation term. Templates are like intermediate classes, waiting for their type parameters so that they can be used. Java takes a different path, which has both benefits and drawbacks that well describe in detail in this chapter. The most compelling case for generics is container classes and collections. But we start to introduce generic methods, which intelligently infer their parameter types based upon how they are invoked. Then we get into the details of writing generic classes and then look at a couple of real-world generic classes in the Java API. Motivation It would be nice if we could write a single sort method that could sort the elements in an Integer array, a String array or an array of any type that supports ordering (i.e., its elements can be compared). It would also be nice if we could write a single Stack class that could be used as a Stack of integers, a Stack of floating-point numbers, a Stack of Strings or a Stack of any other type. It would be even nicer if we could detect type mismatches at compile time—known as compile-time type safety. For example, if a Stack stores only integers, attempting to push a String on to that Stack should issue a compile-time error. Generic methods and generic classes enable programmers to specify, with a single method declaration, a set of related methods or, with a single class declaration, a set of related types, respectively. Generics also provide compile-time type safety that allows programmers to catch invalid types at compile time. We might write a generic method for sorting an array of objects, then invoke the generic method with Integer arrays, Double arrays, String arrays and so on, to sort the array elements. The compiler could perform type checking to ensure that the array passed to the sorting method contains same type elements. We might write a single generic Stack class that manipulates a stack of objects, then instantiate Stack objects for a stack of Integers, a stack of Doubles, a stack of Strings and so on. The compiler could perform type checking to ensure that the Stack stores elements of the same type. Generic Methods Example Generics 1 To motivate generic methods we use this example that contains three overloaded printArray methods. Overloaded methods are often used to perform similar operations on different types of data. These methods print the string representations of the elements of an Integer array, a Double array and a Character array, respectively. Note that we could have used arrays of primitive types int, double and char in this example. We chose to use arrays of type Integer, Double and Character (type-wrapper class) to set up our generic method example, because only reference types can be used with generic methods and classes. public class OverloadedMethods { //Method printArray to print Integer array public static void printArray( Integer[] inputArray ) { 37 IOPROG – Ingmar Olsson Begin with Java 2 //Display array elements, using "for-each" loop: "Begin with Java”, p 23 for (Integer element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } //Method printArray to print Double array public static void printArray(Double[] inputArray ) { //Display array elements for ( Double element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } //Method printArray to print Character array public static void printArray(Character[] inputArray ) { //Display array elements for ( Character element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } public static void main( String args[] ) { //Create arrays of Integer, Double and Character Integer[] integerArray = { 1, 2, 3, 4, 5, 6 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; Character[] characterArray = { 'H', 'E', 'L', 'L', 'O' }; System.out.println( "Array integerArray contains:" ); printArray(integerArray ); System.out.println( "\nArray doubleArray contains:" ); printArray(doubleArray ); System.out.println( "\nArray characterArray contains:" ); printArray(characterArray ); } } If the operations performed by several overloaded methods are identical for each argument type, the overloaded methods can be more compactly and conveniently coded using a generic method. You can write a single generic method declaration that can be called with arguments of different types. Based on the types of the arguments passed to the generic method, the compiler handles each method call appropriately. Following are the rules to define Generic Methods: All generic method declarations have a type parameter section delimited by angle brackets (< and >) that precedes the method's return type ( <T> in the next example). Each type parameter section contains one or more type parameters separated by commas. A type parameter, also known as a type variable, is an identifier that specifies a generic type name. - The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments. A generic method's body is declared like that of any other method. Note that type parameters can represent only reference types not primitive types (like int, double and char). Example Generics 2 38 IOPROG – Ingmar Olsson Begin with Java 2 This example reimplements the application in Example Generics 1 using a generic printArray method. Note that the printArray method calls are identical to those in Example Generics 1 and that the outputs of the two applications are identical. This dramatically demonstrates the expressive power of generics. public class GenericMethod { //Generic method printArray, using "for-each" loop: "Begin with Java”, p 23 public static <T> void printArray( T[] inputArray ) { //Display array elements for ( T element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } public static void main(String args[]) { //Create arrays of Integer, Double and Character Integer[] integerArray = { 1, 2, 3, 4, 5 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; Character[] characterArray = { 'H', 'E', 'L', 'L', 'O' }; System.out.println( "Array integerArray contains:" ); printArray( integerArray ); System.out.println( "\nArray doubleArray contains:" ); printArray( doubleArray ); System.out.println( "\nArray characterArray contains:" ); printArray( characterArray ); } } Generic method syntax The syntax definition of a (static void) generic method is public static <T> void <methodName>(<T-datatype> <parameterName>) The definition starts with a placeholder or a type-parameter section <T> in which T is optional name for the type parameter but a common selection (it could be Type, E, …). Other parts are similar to non-generic methods as stated in “Begin with Java 1”, page 28. In Example Generics 2 this is realized as the method signature public static <T> void printArray(T[] inputArray ) A generic method can also have a type parameter as a return type. The syntax definition of a (static returnType) generic method is public static <T> returnType <methodName>(<T-datatype> <parameterName>) where the returnType could be a non-generic datatype or a generic type parameter T As an example of a generic method with returntype we can take the method max which returns the largest of three T-datatype object parameters. The relational operator < can not be used with reference types so we have to use the interface Comparable<T> for type parameter T to be able to use the built-in method compareTo. This restriction or constraint for the type parameter T is called an upper bound type parameter and is expressed with the keyword extends in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces). 39 IOPROG – Ingmar Olsson Begin with Java 2 Then the signature for the generic max method can be written public static <T extends Comparable<T>> T max(T x, T y, T z) and the generic max method public static <T extends Comparable<T>> T max(T x, T y, T z) { T m = x; if (y.compareTo(m) > 0 m = y; if (z.compareto(m) > 0) m = z; return m; } Example Generics 3 The generic method max can be used like this public class GenericMethod { public static void main(String[] args) { System.out.printf("Maximum of %d, %d and %d is %d\n\n", 3,4,5, max(3,4,5)); System.out.printf("Maximum of %.1f, %.1f and %.1f is %.1f\n\n", 6.6, 7.7, 8.8, max(6.6,7.7,8.8)); System.out.printf("Maximum of %s, %s and %s is %s\n", "Nisse", "Sven", "Allan", max("Nisse", "Sven", "Allan")); } public static <T extends Comparable<T>> T max(T x, T y, T z) { T m = x; if (y.compareTo(m) > 0) m = y; if (z.compareTo(m) > 0) m = z; return m; } } The output is Maximum of 3, 4 and 5 is 5 Maximum of 6,6, 7,7 and 8,8 is 8,8 Maximum of Nisse, Sven and Allan is Sven Type Erasure When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a metod or a class. By default all generic types are replaced with the root type Object. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics. Example Generics 4 40 IOPROG – Ingmar Olsson Begin with Java 2 Here is a generic version of the Bubble sort method with the same algoritm as the non-generic version, just a new generic signature and given the local tTemp variable a generic datatype, from “Begin with Java 1”, Example Array 3, page 38. static <T extends Comparable<T>> void bubbleSortGeneric(T[] gArray, int n) { int i,j; T tTemp; for (i = 0; i < n-1; i++) for (j = n-1; j >= i+1; j--) if (gArray[j-1].compareTo(gArray[j]) > 0) { tTemp = gArray[j-1]; gArray[j-1] = gArray[j]; gArray[j] = tTemp; } } public static <T> void printArray(T[] gArray, int n) { int i = 0; for (T element : gArray) { System.out.printf("%s ", element); if ((i % 20 == 0) && (i > 0)) System.out.println(); i++; } System.out.println(); } public static void slumptal(Integer[] a, int n) { Random r = new Random(); for (int i = 0; i <= n-1; i++) a[i] = Math.abs(r.nextInt()) % 1000; } public static void stringInput(String[] s, int n) { Scanner input = new Scanner(System.in); for (int i = 0; i < n; i++) { System.out.print("String " + (i+1) + ": "); s[i] = input.next(); } } This generic sort method can be used for Inte ger and String datatypes like this. public static void main(String args[]) { System.out.print("Give the number of integers to be sorted: "); Scanner input = new Scanner(System.in); int antal; antal = input.nextInt(); Integer[] helTal = new Integer[antal]; slumptal(helTal, antal); System.out.println("Unsorted: "); printArray(helTal, antal); 41 IOPROG – Ingmar Olsson Begin with Java 2 bubbleSortGeneric(helTal, antal); System.out.println("Sorted: "); printArray(helTal,antal); /* System.out.print("Give number of strings: "); Scanner input = new Scanner(System.in); int antal; antal = input.nextInt(); String[] s = new String[antal]; stringInput(s, antal); System.out.println("Unsorted: "); printArray(s, antal); bubbleSortGeneric(s, antal); System.out.println("Sorted: "); printArray(s,antal); */ } 42 IOPROG – Ingmar Olsson Begin with Java 2 Generic Classes A generic class declaration looks like a non-generic class declaration, except that the class name is followed by a placeholder or type parameter section <T>. As with generic methods, the type parameter section of a generic class can have one or more type parameters separated by commas. These classes are known as parameterized classes or parameterized types because they accept one or more parameters. Generic class motivation (again) A common example with non-generic datatypes is the following. Suppose a class Buffer: class Buffer { private Object[] data; public void put(Object o) {...} public Object get() {...} } This can result in problems, even when the root class Object is used: Type casts needed: buffer.put(3); // Boxing imposes run-time costs int x = (int)buffer.get(); // Type cast imposes run-time costs One cannot statically enforce homogeneous data structures buffer.Put(3); buffer.put(new Rectangle()); Rectangle r = (Rectangle)buffer.get(); // Can result in a run-time error! Special types IntBuffer, RectangleBuffer, ... introduce redundancy Generic classes cure those problems by providing Type-safe code Performance No code bloat Constraints More restrictive object model Example Generics 5 Here is a simple example on a generic class (without defined constructor(s)) public class GenericClass<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { GenericClass<Integer> integerClass = new GenericClass<Integer>(); GenericClass<String> stringClass = new GenericClass<String>(); 43 IOPROG – Ingmar Olsson Begin with Java 2 integerClass.add(new Integer(10)); stringClass.add(new String("Hello World")); System.out.printf("Integer Value: %d\n\n", integerClass.get()); System.out.printf("String Value: %s\n", stringClass.get()); } } The instantiation of objects of the class is made by the keyword new as for non-generic classes with the <T> placeholder/type parameter changed to the wanted datatype. As we said before the instantiation must use the reference datatypes, the type-wrapper classes (in package java.lang), not the primitive datatypes. That means Integer, Short, Long, Float, Double, Byte, Character, Boolean classes. Those classes enable the primitive types to be manipulated as objects. The output is: Integer Value: 10 String Value: Hello World Example Generics 6 This example demonstrate a non-generic stack public class NonGeneric_Stack { public static void main(String args[]) { NonGeneric integerObject; integerObject = new NonGeneric(88); integerObject.showType(); int v = (Integer) integerObject.pop(); System.out.println("Integer value: " + v); NonGeneric strOb = new NonGeneric("String test"); strOb.showType(); String str = (String) strOb.pop(); System.out.println("String value: " + str); //integerObject = strOb; //v = (Integer) integerObject.pop(); } } public class NonGeneric { Object ob; NonGeneric(Object o) { ob = o; } Object pop() { return ob; } void showType() { System.out.println("Type of ob is " + ob.getClass().getName()); 44 IOPROG – Ingmar Olsson Begin with Java 2 } } The output is: Type of ob is java.lang.Integer Integer value: 88 Type of ob is java.lang.String String value: String test Take away the comments in the statements and execute again! //integerObject = strOb; //v = (Integer) integerObject.pop(); Example Generics 7 This example demonstrates a generic version of the stack in Example Generics 6. public class GenericStack { public static void main(String args[]) { Generic<Integer> intGen; intGen = new Generic<Integer>(88); intGen.showType(); int v = intGen.pop(); System.out.println("Integer value: " + v); System.out.println(); Generic<String> strGen = new Generic<String>("String test"); strGen.showType(); String str = strGen.pop(); System.out.println("String value: " + str); } } public class Generic<T> { T obj; //Declare an object of type T Generic(T object) { obj = object; } T pop() { return obj; } void showType() 45 IOPROG – Ingmar Olsson Begin with Java 2 { System.out.println("Type of T is " + obj.getClass().getName()); } } The output is: Type of T is java.lang.Integer Integer value: 88 Type of T is java.lang.String String value: String test Exercise Generics 1 This is an implementation of a non-generic sorting algoritm called InsertionSort. Implement a generic version of this sorting algoritm. import java.util.Random; public class InsertionSort { private int[] data; private static Random generator = new Random(); public InsertionSort( int size ) { data = new int[ size ]; // Fill array with random ints in range 10-99 for ( int i = 0; i < size; i++ ) data[ i ] = 10 + generator.nextInt( 90 ); } // Sort array using selection sort public void sort() { int insert; for ( int next = 1; next < data.length; next++ ) { insert = data[ next ]; int moveItem = next; while ( moveItem > 0 && data[ moveItem - 1 ] > insert ) { data[ moveItem ] = data[ moveItem - 1 ]; moveItem--; } data[ moveItem ] = insert; // place inserted element printPass( next, moveItem ); // output pass of algorithm } } public String toString() { StringBuilder temporary = new StringBuilder(); 46 IOPROG – Ingmar Olsson Begin with Java 2 for ( int element : data ) temporary.append( element + " " ); temporary.append( "\n" ); return temporary.toString(); } } Exercise Generics 2 This is an implementation of a non-generic Stack (with the elements in the Stack (class Entry) made as a linked list). Implement a generic version of this Stack. public class Stack { Entry top; public void push(Object data) { top = new Entry(top, data); } public Object pop() { if (top == null) throw new NullPointerException(); Object result = top.data; top = top.next; return result; } class Entry { public Entry next; public Object data; public Entry(Entry next, Object data) { this.next = next; this.data = data; } } } public class NonGeneric_Stack { public static void main(String[] args) { Stack s = new Stack(); s.push(1); s.push(10); s.push(100); System.out.println(s.pop()); System.out.println(s.pop()); //System.out.println(s.pop()); Integer num = (Integer)s.pop(); System.out.println(num); //String number = (String)s.pop(); //System.out.println(number); } } 47 IOPROG – Ingmar Olsson Begin with Java 2 Use the generic Stack on class Integer. public class GenericStack { public static void main(String[] args) { Stack<Integer> s = new Stack<Integer>(); s.push(1); s.push(10); s.push(100); System.out.println(s.pop()); System.out.println(s.pop()); System.out.println(s.pop()); } } public class GenericStack { public static void main(String[] args) { Stack<Person> sPerson = new Stack<Person>(); sPerson.push(new Person("Pers1")); sPerson.push(new Person("Pers2")); sPerson.push(new Person("Pers3")); System.out.println(sPerson.pop().toString()); System.out.println(sPerson.pop().toString()); System.out.println(sPerson.pop().toString()); } } Use the Stack on the user defined class Person public class Person { private String namn; private long anstNummer; static long aNummer = 1000; //Initialt anställningsnummer public Person(String anstNamn) { namn = anstNamn; //Call to get anstNummer anstNummer = KonstrueraAnstNummer(); } public long VisaAnstNummer() { return anstNummer; } public String VisaNamn() { return namn; 48 IOPROG – Ingmar Olsson Begin with Java 2 } public static long KonstrueraAnstNummer() { return aNummer++; } public String toString() { //System.out.printf("%s %s\n",namn, anstNummer); String s = "Namn: " + namn + " " + "Anst nr: " + anstNummer; return s; } } Use the following client program. public class GenericStack { public static void main(String[] args) { Stack<Person> sPerson = new Stack<Person>(); sPerson.push(new Person("Pers1")); sPerson.push(new Person("Pers2")); sPerson.push(new Person("Pers3")); System.out.println(sPerson.pop().toString()); System.out.println(sPerson.pop().toString()); System.out.println(sPerson.pop().toString()); } } Wildcards – Wildcard-type argument Java has several predefined data structures, called Collections, used to organize, store and retrieve data. One of those Collection classes is ArrayList which exist in both a no-generic and generic, ArrayList<T>, version. This is examined in the next chapter “Java and Database Technique without Database – ArrayList”. To examine the wildcard-type argument denoted by a question mark, ?, “unknown type”, the generic ArrayList<T> will be used a little in advance. If any type-wrapper datatype (Integer, Double, …) is used there is a built-in subclass for all those classes called Number. The abstract class Number in java.lang library is the superclass of classes Byte, Double, Float, Integer, Long, and Short. Subclasses of Number must provide methods to convert the represented numeric value to byte, double, float, int, long, and short. Example Generics 8 In this example the datastructure ArrayList<Number> is used in a generic method sum to calculate the sum of different numbers. import java.util.ArrayList; 49 IOPROG – Ingmar Olsson Begin with Java 2 public class NonWildcard { public static void main( String args[] ) { //Number[] contains both Integers and Doubles Number[] numbers = { 1, 2.4, 3, 4.1 }; ArrayList< Number > numberList = new ArrayList< Number >(); for ( Number element : numbers ) numberList.add( element ); System.out.printf( "The list of numbers contains: %s\n", numberList ); System.out.printf( "Total of the elements in numberList: %.1f\n", sum( numberList ) ); } //Calculate sum public static double sum( ArrayList< Number > list ) { double total = 0; // initialize total for ( Number element : list ) total += element.doubleValue(); return total; } } Example Generics 9 If we want Number to contain any subclass to Number, Integer, Double, … we can use the wildcard to extend the Number class and get the wildcard-type argument ? to denote an upper bound to Number. This is done with generic type: ArrayList<? extends Number> import java.util.ArrayList; public class Wildcard { public static void main( String args[] ) { //ArrayList of Integers Integer[] integers = { 1, 2, 3, 4, 5 }; ArrayList< Integer > integerList = new ArrayList< Integer >(); for ( Integer element : integers ) integerList.add( element ); System.out.printf( "IntegerList contains: %s\n", integerList ); System.out.printf( "Total of the elements in integerList: %.0f\n\n", sum( integerList ) ); //ArrayList of Doubles Double[] doubles = { 1.1, 3.3, 5.5 }; ArrayList< Double > doubleList = new ArrayList< Double >(); for ( Double element : doubles ) doubleList.add( element ); System.out.printf( "DoubleList contains: %s\n", doubleList ); System.out.printf( "Total of the elements in doubleList: %.1f\n\n", sum( doubleList ) ); //ArrayList of Numbers containing both Integers and Doubles Number[] numbers = { 1, 2.4, 3, 4.1 }; ArrayList< Number > numberList = new ArrayList< Number >(); 50 IOPROG – Ingmar Olsson Begin with Java 2 for ( Number element : numbers ) numberList.add( element ); System.out.printf( "numberList contains: %s\n", numberList ); System.out.printf( "Total of the elements in numberList: %.1f\n", sum( numberList ) ); } //Calculate sum public static double sum( ArrayList< ? extends Number > list ) { double total = 0; // initialize total for ( Number element : list ) total += element.doubleValue(); return total; } } 51 IOPROG – Ingmar Olsson Begin with Java 2 Java and Database Technique without Database – ArrayList To handle and store data in volume you need to have a database software (database manager and a database engine) that can be installed and connected to the development environment and running system. To handle smaller amount of data and less complex data it can be suitable to use a powerful and generic Collection datastructure like ArrayList as an in-memory database and use a simple textfile technique for keeping the data permanent. The next sections Non-generic ArrayList and Generic/Parameterized Type ArrayList contain basic information of how to use ArrayList as a surrogate database. Non-generic ArrayList The built-in JDK class java.util.ArrayList allows for expandable arrays and has the following advantages over an array: An ArrayList automatically expands as data is added. Access to any element of an ArrayList is O(1). Insertions and deletions are O(N). An ArrayList has methods for inserting, deleting, and searching. An ArrayList can be traversed using either iterators or indexes. Automatic expansion. Use ArrayList when there will be a large variation in the amount of data that you would put into an array. Arrays should be used only when there is a constant amount of data. For example, storing information about the days of the week should use an array because the number of days in a week is constant. Use an array list for your email contact list because there is no upper bound on the number of contacts. Objects only. A disadvantage of a ArrayList is that it holds only objects and not primitive types (eg, int). To use a primitive type in an ArrayList, put it inside an object (eg, to save an integer value use the Integer class or define your own class). If you use the Integer wrapper, you will not be able to change the integer value, so it is sometimes useful to define your own class. Implementation. ArrayLists are implemented with an underlying array, and when that array is full and an additional element is added, a new array is allocated and the elements are copied from the old to the new. Because it takes time to create a bigger array and copy the elements from the old array to the new array, it is a faster to create an ArrayList with a size that it will commonly be when full. Of course, if you knew the final size, you could simply use an array. However, for non-critical sections of code programmers typically don't specify an initial size. Constructors ArrayList alist1 = new ArrayList(); // Created with default size. ArrayList alist2 = new ArrayList(300); // Starts with 300 elements To Add elements to the end of an ArrayList a.add(s); // adds s to the end of the ArrayList a To get the elements from an ArrayList Use either a for loop with an integer index to get all the elements from an ArrayList, or go over all elements in a ArrayList using an Iterator (forward) or ListIterator (forward / backward). for loop with index - this is the fastest method, but will need to be changed you later decide to change to a LinkedList structure. ListIterator - Allows traversal of the ArrayList, and allows inserts and deletes (although both are very slow for an ArrayList). It also allows bidirectional traversal. Can be used if the data structure is later changed. Iterator - Allows simple forward traversal. Can be used for the largest number of other kinds of data structures. This example uses an Iterator to print all elements (Strings) in an ArrayList. It uses hasNext(), which returns true if there are more elements, and next(), which returns the next element as an Object, which must be downcast to the appropriate type (until Java 1.5 additions). // Without Java 5 generics. for (Iterator iter = a.iterator(); iter.hasNext();) { 52 IOPROG – Ingmar Olsson Begin with Java 2 System.out.println((String)(iter.next())); } The following is faster, but less general: // Without Java 5 generics. for (int i = 0; i < a.size(); i++) { System.out.println((String)(a.get(i))); } Sorting If the data in your ArrayList has a natural sorting order (ie, implements Comparable, as do String, Integer, ...), you can simply call the static Collections.sort() method. This is a stable, guaranteed n log n sort. Collections.sort(yourArrayList); If you want to choose a different sort criterion or your data doesn't implement xxxx, you will have to define a Comparator and pass that to the sort() method. Collections.sort(yourArrayList, yourComparator); Check out Collections for other useful utility methods. Common ArrayList methods and constructors. Here are some of the most useful ArrayList methods. a is an ArrayList, i is an int, obj is an Object, iter is an Iterator, liter is a ListIterator. Result Method Description Constructors a= a= a= new ArrayList() new ArrayList(cap) new ArrayList(coll) Creates ArrayList with initial default capacity 10. Creates ArrayList with initial int capacity cap. Creates ArrayList from the Collection coll. a.add(obj) a.add(i, obj) adds obj to end of ArrayList a Inserts obj at index i, shifting elements up as a.set(i,obj) Sets the element at index i to obj. Adding elements necessary. Replacing an element Getting the elements obj = a.get(i) Returns the object at index i. oarray = a.toArray() Returns values in array. oarray = a.toArray(Object[]) The array parameter can be any Object subclass (eg, String). This returns values in that array (or a larger array if necessary). Iterators iter = a.iterator() Returns an Iterator for forward traversal. liter = a.listIterator(i) Returns a ListIterator for forward / backward / modifying traversal, starting at index i. Start from end with a.listIterator(a.size()) liter = a.listIterator() Returns a ListIterator for forward / backward / modifying traversal. Searching b= a.contains(obj) Returns true if ArrayList a contains obj i= a.indexOf(obj) Returns index of first occurrence of obj, or -1 if not there. i= a.lastIndexOf(obj) Returns index of last occurrence of obj, or -1 if not there. Removing elements Other i= a.clear() a.remove(i) a.removeRange(i, j) removes all elements from ArrayList a Removes the element at position i. Removes the elements from positions i thru j. a.size() Returns the number of elements in ArrayList a. 53 IOPROG – Ingmar Olsson Begin with Java 2 Generic/Parameterized Type ArrayList JDK 5.0 provides generic or parameterized typed array list. Before JDK 5.0 Array List store the objects of type Object. But now the type of the elements of an ArrayList can be specified. This includes a very significant addition of generics feature in which object containers can actually know what object type they should hold. Then any attempt to insert an object of the wrong type results in a compile-time error rather than a runtime ClassCastException. This generic type notation is also known as a parameterized type. Syntax: Type parameter naming: T, U, ... Although any names are possible for type parameters, they are traditionally written in upper case and often taken from the sequence T, U, V, ... Other upper case letters are used when they have specific meanings, eg, K for the type of a key, E for element type. Reading type parameters Notation List<T> List<?> Meaning List of elements of type T (T is a concrete type) List of any type (? is an unbounded wildcard type) Example Database-ArrayList 1 import java.util.ArrayList; public class MyArrayList { public static void main(String[] args) { Seller sel = new Seller(); Buyer buy = new Buyer(); // Declare an array list of type Seller ArrayList<Seller> list = new ArrayList<Seller>(); list.add(sel); //Add object of type Seller which is permissible list.add(buy); //This statement will give an error } } Example Database-ArrayList 2 // Purpose: An example using Scanner, ArrayList, and Collections.sort. // Read words, sort them, print them. import java.util.*; public class Alphabetize { public static void main(String[] args) { //... Declare variables. Scanner in = new Scanner(System.in); ArrayList<String> words = new ArrayList<String>(); //... Read input one word at a time. System.out.println("Enter words. End with EOF (CTRL-Z then Enter)"); System.out.println(" or click Close Input in NetBeans."); //... Read input one word at a time, adding it to an array list. while (in.hasNext()) { words.add(in.next()); } 54 IOPROG – Ingmar Olsson Begin with Java 2 //... Sort the words. Collections.sort(words); //... Print the sorted list. System.out.println("\n\nSorted words\n"); for (String word : words) { System.out.println(word); } } } java.util.ArrayList<E> allows for expandable arrays. An ArrayList has these characteristics: An ArrayList automatically expands as data is added. Access to any element of an ArrayList is O(1). Insertions and deletions are O(N). An ArrayList has methods for inserting, deleting, and searching. An ArrayList can be traversed using a foreach loop, iterators, or indexes. Arrays or ArrayList? Programmers are frequently faced with the choice of using a simple array or an ArrayList. If the data has a known number of elements or small fixed size upper bound, or where efficiency in using primitive types is important, arrays are often the best choice. However, many data storage problems are not that simple, and ArrayList (or one of the other Collections classes) might be the right choice. Automatic expansion. Use ArrayList when there will be a large variation in the amount of data that you would put into an array. Arrays should be used only when there is a constant amount of data. For example, storing information about the days of the week should use an array because the number of days in a week is constant. Use an array list for your email contact list because there is no upper bound on the number of contacts. Objects only. A possible disadvantage of ArrayList is that it holds only object types and not primitive types (eg, int). To use a primitive type in an ArrayList, put it inside an object or use of the wrapper classes (eg, Integer, Double, Character, ...). The wrapper classes are immutable, so if you use, eg, Integer, you will not be able to change the integer value. In this case it may be more useful to define your own mutable class. Implementation. ArrayLists are implemented with an underlying array, and when that array is full and an additional element is added, a new, larger, array is allocated and the elements are copied from the old to the new. Because it takes time to create a bigger array and copy the elements from the old array to the new array, it is a slightly faster to create an ArrayList with a size that it will commonly be when full. Of course, if you knew the final size, you could simply use an array. However, for non-critical sections of code programmers typically don't specify an initial size. Common ArrayList methods and constructors Here are some of the most useful ArrayList methods. Assume these declarations. Note that E is the notation for the type of an element in a collection. Sun recommends using single upper case letters for generic types. int i; ArrayList<E> a; E e; Iterator<E> iter; ListIterator<E> liter; E[] earray; Object[] oarray; Result Method Constructors a= a= a= new ArrayList<E>() Creates ArrayList with initial default capacity 10. new ArrayList<E>(cap) Creates ArrayList with initial int capacity cap. new ArrayList<E>(coll<E>) Creates ArrayList from the Collection coll. Adding elements up as necessary. Replacing an element Description a.add(e) a.add(i, e) adds e to end of ArrayList a Inserts e at index i, shifting elements a.set(i,e) Sets the element at index i to e. 55 IOPROG – Ingmar Olsson Begin with Java 2 Getting the elements e= a.get(i) Returns the object at index i. oarray = a.toArray() Returns values in array of objects. earray = a.toArray(E[]) The array parameter should be of the E class. Returns values in that array (or a larger array is allocated if necessary). Iterators iter = a.iterator() Returns an Iterator for forward traversal. liter = a.listIterator(i) Returns a ListIterator for forward / backward / modifying traversal, starting at index i. Start from end with a.listIterator(a.size()) liter = a.listIterator() Returns a ListIterator for forward / backward / modifying traversal. Searching b= i= if not there. i= if not there. Removing elements Other i= ArrayList a. a.contains(e) a.indexOf(e) Returns true if ArrayList a contains e Returns index of first occurrence of e, or -1 a.lastIndexOf(e) Returns index of last occurrence of e, or -1 a.clear() a.remove(i) a.removeRange(i, j) a.size() Removes all elements from ArrayList a Removes the element at position i. Removes the elements from positions i thru j. Returns the number of elements in Adding elements to the end of an ArrayList, getting them by index ArrayList<E> a = new ArrayList<E>(); // Default size. E s; // Declare s to be an object type E. ... a.add(s); // Adds s to the end of the ArrayList a ... s = a.get(i); // Assigns ith element from a to s. To get successive elements from an ArrayList - Four ways Use either a for loop with an integer index to get all the elements from an ArrayList, or go over all elements in a ArrayList using an Iterator (forward) or ListIterator (forward / backward). 1. foreach loop. This is fast and works for all kinds of lists, but is not entirely flexible (only sequential forward with no deletions, additions, or multiple references). This should be your first choice in programming. Works efficiently with both ArrayList and LinkedList. ArrayList<String> a = new ArrayList<String>(); . . . for (String s : a) { System.out.println(s); } 2. for loop with index. This is fast, but should not be used with a LinkedList. It does allow orders other than sequentially forward by one. for (int i = 0; i < a.size(); i++) { System.out.println(a.get(i)); } 3. Iterator<E> - Allows simple forward traversal. Can be used for the largest number of other kinds of data structures. 56 IOPROG – Ingmar Olsson Begin with Java 2 This example uses an Iterator to print all elements (Strings) in an ArrayList. It uses hasNext(), which returns true if there are more elements, and next(), which returns the next element. Works with both ArrayList and LinkedList. for (Iterator<String> iter = a.iterator(); iter.hasNext(); { System.out.println(iter.next()); } ) 4. ListIterator<E> - Allows traversal of the ArrayList, but it is more general than a simple Iterator, allowing inserts and deletes (although both are very slow for an ArrayList). It also allows bidirectional traversal. Works efficiently with both ArrayList and LinkedList. Sorting If the data in your ArrayList has a natural sorting order (ie, implements Comparable, as do String, Integer, ...), you can simply call the static Collections.sort() method. This is a stable, guaranteed n log n sort. Collections.sort(yourArrayList); If you want to choose a different sort criterion or your data doesn't implement xxxx, you will have to define a Comparator and pass that to the sort() method. Collections.sort(yourArrayList, yourComparator); Check out Collections for other useful utility methods. Console Menu In all applications, small or huge, it is necessary to have a robust and user friendly user interface. This can be a TUI (Textual User Interface) or a GUI (Graphic User Interface). Here is a skeleton for a TUI or a console menu that is suitable for many applications. GUI will be covered in future chapters. The menu alternative and the activities are optional and are adopted to the application. //Menu.java import java.util.*; class AddCustomer { public AddCustomer() { System.out.println("AddCustomer not implemented"); } } class AddItem { public AddItem() { System.out.println("AddItem not implemented"); } } class AddRental { public AddRental() { System.out.println("AddRental not implemented"); } } class UpdateProduct { public UpdateProduct() { System.out.println("UpdateProduct not implemented"); } } 57 IOPROG – Ingmar Olsson Begin with Java 2 class DeleteProduct { public DeleteProduct() { System.out.println("DeleteProduct not implemented"); } } class ViewProduct { public ViewProduct() { System.out.println("ViewProduct not implemented"); } } public class Menu { public static void main(String[] args) throws Exception { Scanner scan = new Scanner(System.in); int menu = 0; boolean quit = false; do { menu(); System.out.println(); System.out.print("Please enter your choice: "); menu = scan.nextInt(); System.out.println(); switch(menu) { case 1: AddCustomer addC = new AddCustomer(); break; case 2: AddItem addI = new AddItem(); break; case 3: AddRental addR = new AddRental(); break; case 4: UpdateProduct update = new UpdateProduct(); break; case 5: DeleteProduct delete = new DeleteProduct(); break; case 6: ViewProduct view = new ViewProduct(); break; case 9: quit = true; break; default: System.out.println("Invalid Entry!"); } } while (!quit); } public static void menu() { System.out.println(); System.out.println("Menu"); System.out.println(); System.out.println("1. Add Customer"); System.out.println("2. Add Item"); System.out.println("3. Add Rental"); System.out.println("4. Update Product"); System.out.println("5. Delete Product"); System.out.println("6. View Product"); System.out.println("7. Write Database (Textfiles)"); 58 IOPROG – Ingmar Olsson Begin with Java 2 System.out.println("8. Read Database (Textfiles)"); System.out.println("9. Exit"); System.out.println(); } This menu/TUI can be exercised like this! 59 IOPROG – Ingmar Olsson Begin with Java 2 An application using ArrayList as a Database Database introduction A database table is a container that holds information about like items. For example, an Customers table would contain the same basic details on each customer: number, name, title, department and so on. Each detail, each chunk of information you need to store lives in a field. When you view a single customer record, you can displays it in a textual print-out or a graphical form. Whenever you want to view more that one record at a time, you do so in a table report, where fields appear as columns and each record is a row. A relationship between tables is usually a one-to-many affair. In other words, one record in the master table relates to many records in the detail table. For example, one course has many students assigned to it. But what if you need to assign MANY students to MANY courses? To accomplish this many-to-many connection, you can add a third table that acts as a go-between or relation and handles all the possible combinations involved. Datamodel You can represent the tables with a picture (datamodel) like this: Customer 1 ∞ CustomerNimber CustomerName many Rental CustomerNumber ItemNumber ∞ many 1 Item ItemNumber ItemName Here are some examples on applications/datamodels where you have to use the many-to-many technique: Customer Customer Borrower Student Pupil Author Rental Order Loan Enrollment Schedule Published Item Product Book Course Class Book Design and Implementation This application is supposed to have functionality for a rental/business company which wants to store and handle information about current rental/order, customer, rental/product items as DVDs, CDs, Mobile Phones, Cars, Tools, TV sets, Toys, … and similar. In the first design step and implementation of the application we use a generic ArrayList as an in-memory database and a textfile for persistency of the data. As a data model we use a Customer class, Rental class and an Item class that can be specialized for different item. Implement the Customer class. Use only two datamembers so far: number and name and the corresponding accessor methods, getCustomerID() and getCustomerName(). Implement the Item class. Datamembers ID and name, accessors getItemID() and getItemName(). Implement the “join”, “many-to-many” Rental class. Datamembers number (key from Customer class) and ID (key from Item class), accessors getCustomerID() and getItemID(). //Customer.java public class Customer { private int number; private String name; public Customer(String n, int i) { number = i; 60 IOPROG – Ingmar Olsson Begin with Java 2 name = n; } public int getCustomerID() { return number; } public String getCustomerName() { return name; } } //Item.java public class Item { private int ID; private String name; public Item (String n, int i) { name = n; ID = i; } public int getItemID() { return ID; } public String getItemName() { return name; } } //Rental.java public class Rental { private int number; private int ID; public Rental (int n , int i) { number = n; ID = i; } public int getItemID() { return ID; } public int getCustomerID() { return number; } } Implement a class Database and use generic/parameterized ArrayLists as the built-in datastructure for the database. //Database.java import java.util.ArrayList; public class Database { private ArrayList<Rental> rental; private ArrayList<Customer> customer; private ArrayList<Item> item; 61 IOPROG – Ingmar Olsson Begin with Java 2 public Database() { rental = new ArrayList<Rental>(); customer = new ArrayList<Customer>(); item = new ArrayList<Item>(); } } To get the persistency for the database use in this application the simpliest form of textfiles handling the read and write functionality with the built-in Java classes Scanner and Formatter. Database monitor To handle the database interface with the user, use a textual user interface (TUI). In a database system this is often called a monitor. In this application it is shaped as a menu. //Menu.java import java.util.*; public class Menu { public static void main(String[] args) { Scanner scan = new Scanner(System.in); Database d = new Database(); int menu = 0; boolean quit = false; do { menu(); System.out.println(); System.out.print("Please enter your choice: "); menu = scan.nextInt(); System.out.println(); switch(menu) { case 1: WriteCustomer_TextFile writeC = new WriteCustomer_TextFile(); writeC.openFile(); writeC.addRecords(); writeC.closeFile(); break; case 2: WriteItem_TextFile writeI = new WriteItem_TextFile(); writeI.openFile(); writeI.addRecords(); writeI.closeFile(); break; case 3: WriteRental_TextFile writeR = new WriteRental_TextFile(); writeR.openFile(); writeR.addRecord(); writeR.closeFile(); break; //case 4: //case 5: //case 6: //case 7: //case 8: case 9: quit = true; break; default: System.out.println("Invalid Entry!"); } } while (!quit); } 62 IOPROG – Ingmar Olsson Begin with Java 2 public static void menu() { System.out.println(); System.out.println("Menu"); System.out.println(); System.out.println("1. Write/Add Customer Database Table (TextFile)"); System.out.println("2. Write/Add Item Database Table (TextFile)"); System.out.println("3. System.out.println("4. System.out.println("5. System.out.println("6. System.out.println("7. System.out.println("8. System.out.println("9. System.out.println(); Write/Add Rental Database Table (TextFile)"); Read Customer Database Table (TextFile)"); Read Item Database Table (TextFile)"); Read Rental Database Table (TextFile)"); Find Customer rentals of Items"); Find Items rented by Customer"); Exit"); } Write/Add Customer Database Table (TextFile) Continue with implementation of the Database Monitor/Menu functionality for 1. Write/Add Customer Database Table (TextFile) case 1: WriteCustomer_TextFile writeC = new WriteCustomer_TextFile(); writeC.openFile(); writeC.addRecords(); writeC.closeFile(); This functionality is included in the class WriteCustomer_TextFile that is instantiated in case 1: alternative in the Menu class. This class implements three methods: void openFile() This method opens the textfile Customer_Textfile.txt (“hard-coded” name, but can be optional) in read mode with the built-in class Scanner. This textfile must exists even if it is empty when the program starts. Otherwise you get the programmed exception. The textfile is created by a texteditor like Notepad, WordPad, xemacs or other texteditor in the same filesystem/folder as the applications project/.java files. The method reads the existing texfile, creates Customer objects with the number and name datamembers and add the objects to an ArrayList. The textfile is closed. The method openFile() now opens the textfile with the same name Customer_TextFile.txt again in write mode with the built-in class Formatter. It will then be overwritten! The method now writes the numbers and names from existing Customer objects in the ArrayList (in-memory database) to the textfile Customer_TextFile.txt (“permanent” database). The textfile is not closed! void addRecords() The method addRecords() now asks the user of the application for new Customer’s number and name and write directly (at the end) to the textfile Customer_TextFile.txt (open in write mode) . void closeFile() The method closeFile() closes the textfile Customer_TextFile.txt. //WriteCustomer_Textfile.java import java.util.ArrayList; import java.io.File; import java.util.Formatter; import java.util.Scanner; import java.io.FileNotFoundException; public class WriteCustomer_TextFile { private Formatter output; private Scanner readFile; 63 IOPROG – Ingmar Olsson Begin with Java 2 public void openFile() { try { //Homemade append //Saves the existing textfile with the object (Customer) datamembers //in an object ArrayList //The textfile must exists and the name of the textfile is "hardcoded" //to "Customer_TextFile.txt" Customer c; int tempNumber; String tempName; ArrayList<Customer> tempList = new ArrayList<Customer>(); readFile = new Scanner( new File( "Customer_TextFile.txt" ) ); while(readFile.hasNext()) { tempNumber = readFile.nextInt(); tempName = readFile.next(); c = new Customer(tempName,tempNumber); tempList.add(c); } readFile.close(); //Open the (same) existing textfile in write mode // The existing textfile will be overwritten //Write the existing datamembers for the objects from the ArrayList output = new Formatter("Customer_TextFile.txt"); for (int i = 0; i <= tempList.size() - 1; i++) { tempName = ((Customer)tempList.get(i)).getCustomerName(); tempNumber = ((Customer)tempList.get(i)).getCustomerID(); output.format("%-10d%-50s\n", tempNumber, tempName); } } catch ( FileNotFoundException filesNotFoundException ) { System.err.println( "Error creating file." ); System.exit( 1 ); } } public void addRecords() { int number; String name; Customer c; //Continue to append (write to end of) the open textfile with new object //datamembers for the Customer class. Scanner input = new Scanner( System.in ); System.out.printf( "%s\n%s\n%s\n%s\n\n", "To terminate input, type the end-of-file indicator ", "when you are prompted to enter input.", "On UNIX/Linux/Mac OS X type <ctrl> d then press Enter", "On Windows type <ctrl> z then press Enter" ); System.out.printf("Enter customer number (> 0), customer name (space between): " ); while ( input.hasNext() ) { number = input.nextInt(); name = input.next(); c = new Customer(name, number); if (number > 0) { output.format("%-10d%-50s\n", c.getCustomerID(), c.getCustomerName()); } else 64 IOPROG – Ingmar Olsson Begin with Java 2 { System.out.println("Account number must be greater than 0." ); } System.out.printf("Enter customer number (> 0), customer name (space between): " ); } } public void closeFile() { if ( output != null ) output.close(); } } Compile the files Database.java, Customer.java, WriteCustomer_TextFile.java and Menu.java. Start and run the program from the compiled class Menu. The resulted Customer_TextFile.txt file is like this 1 2 3 Nisse Allan Sven This textfile can be opened/read as usual by Notepad, WordPad, xemacs or other texteditor directly from the filesystem/folder where the applications projectfiles/.java files are located. Exercise Database-ArrayList 1 Implement the Database Monitor/Menu functionality for 2. Write/Add Item Database Table (TextFile) case 2: WriteItem_TextFile writeI = new WriteItem_TextFile(); writeI.openFile(); writeI.addRecords(); writeI.closeFile(); This implementation is entirely similar to the implementation of WriteCustomer_TextFile class. 65 IOPROG – Ingmar Olsson Begin with Java 2 Exercise Database-ArrayList 2 Implement the Database Monitor/Menu functionality for 3. Write/Add Rental Database Table (TextFile) case 3: WriteRental_TextFile writeR = new WriteRental_TextFile(); writeR.openFile(); writeR.addRecord(); writeR.closeFile(); The implementation of the class WriteRental_TextFile shall cover the “many-to-many” connection between the Customer and Item table/class . This class contains the methods void openFile(); void addRecord(); void writeR.closeFile(); The method void openFile() needs to have the same append functionality for Rental_TextFile.txt as in class WriteCustomer_TextFile and in class WriteItem_TextFile. Then it needs to have methods like int findCustomer() and int findItem() to decide what Item (number) a given Customer (number) wants to rent, the “many-to-many” connection. This Customer-Item connection is then written to Rental_TextFile.txt by the method void addRecord(). This method is not the same as the earlier methods void addRecords()! Exercise Database-ArrayList 3 Implement the Database Monitor/Menu functionality for 4. Read Customer Database Table (TextFile) 5. Read Item Database Table (TextFile) 6. Read Rental Database Table (TextFile) Exercise Database-ArrayList 4 Implement the Database Monitor/Menu functionality for 7. Find Customer Rentals of Items Exercise Database-ArrayList 5 Implement the Database Monitor/Menu functionality for 8. Find Items rented by Customer 66