Download Old (before 2009) exercise notes from Bochum

Document related concepts
no text concepts found
Transcript
Modern Programming Concepts in Engineering
Exercise Notes
Matthias Baitsch
October 2008
2
Contents
1 Our Computer Labs
5
2 Fundamental Programming Structures in Java
2.1 The Programming Environment Eclipse . .
2.2 A Minimalistic Java Program . . . . . . . .
2.2.1 Reviewing Hello World . . . . . . .
2.3 Primitive Types . . . . . . . . . . . . . . .
2.4 Variables and Assignments . . . . . . . . .
2.5 Operators . . . . . . . . . . . . . . . . . .
2.6 Methods and Control Structures . . . . . .
2.6.1 Sequential Steps . . . . . . . . . .
2.6.2 Method . . . . . . . . . . . . . . .
2.6.3 Selection . . . . . . . . . . . . . .
2.6.4 Indeterminate Loops . . . . . . . .
2.7 Java Keywords . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
10
11
12
12
13
15
15
15
16
17
19
3 Objects and classes
3.1 Using existing classes . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1 Three-dimensional objects . . . . . . . . . . . . . . . . . . .
3.1.2 Another practical example: Plotting a function R2 → R . . .
3.2 Program your own classes . . . . . . . . . . . . . . . . . . . . . . .
3.2.1 A vector class . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.2 Practical example: Complex numbers and the Mandelbrot set .
3.3 Computer memory, variables and objects . . . . . . . . . . . . . . . .
3.4 Strings in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5 Understanding Java arrays . . . . . . . . . . . . . . . . . . . . . . .
3.6 Static methods and constants . . . . . . . . . . . . . . . . . . . . . .
3.6.1 Static methods . . . . . . . . . . . . . . . . . . . . . . . . .
3.6.2 Static attributes . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
21
21
24
25
25
33
37
39
39
41
41
41
4 Inheritance and Polymorphism
4.1 Extending Classes . . . . . . . . . . . . . . . . . . . . . .
4.1.1 Using the Base Class Instead of the Derived Class
4.2 Abstract Classes . . . . . . . . . . . . . . . . . . . . . . .
4.2.1 Polymorphism . . . . . . . . . . . . . . . . . . .
4.2.2 The Three Pillars of Object-Oriented Programming
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
43
43
46
47
49
49
3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
4
4.3
4.4
4.2.3 Abstract Classes in Finite Element Programming . . . . . . . . . . . . . . .
Practical Example: Sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5 Connecting Objects
5.1 Programming Associations / Aggregation / Composition
5.2 Catalog of Books for a Library . . . . . . . . . . . . . .
5.2.1 Java Implementation of the Classes . . . . . . .
5.3 Truss Structures . . . . . . . . . . . . . . . . . . . . . .
5.3.1 Designing the classes . . . . . . . . . . . . . . .
5.3.2 Assignment: Implementing the Classes in Java .
5.3.3 Reading a Structure from a File . . . . . . . . .
5.3.4 Example for an Object Diagram . . . . . . . . .
6 Programming Graphical User Interfaces
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . .
6.2 Swing Components . . . . . . . . . . . . . . . . . . .
6.2.1 Using Swing Components . . . . . . . . . . .
6.3 Laying out Components . . . . . . . . . . . . . . . . .
6.3.1 Combining Layout Managers . . . . . . . . .
6.4 Event Handling . . . . . . . . . . . . . . . . . . . . .
6.5 The Model-View-Controller Pattern . . . . . . . . . .
6.5.1 Step 1: Preliminaries . . . . . . . . . . . . . .
6.5.2 Software Design . . . . . . . . . . . . . . . .
6.5.3 Implementation . . . . . . . . . . . . . . . . .
6.5.4 Creating Nodes and Elements Using the Mouse
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
50
53
.
.
.
.
.
.
.
.
55
55
56
57
59
60
61
63
64
.
.
.
.
.
.
.
.
.
.
.
69
69
70
71
72
73
75
77
78
82
83
88
Chapter 1
Our Computer Labs
IA6/56
IA6/164
IA
IB
IC
ICFW03/255
Figure 1.1: Locations of the Labs
The Institute of Computational Engineering (ICE) manages three computer labs: two in the building
IA on the sixth floor (8 PCs in room 6/164 and 12 PCs in room 6/56) and one in the building ICFW
03/255 (23 PCs). This makes a total of 43 computers.
Software The computers run under Windows XP and Linux. All computers have identical software
installed including Ansys, Maple, Mathematica, Microsoft Office, AutoCAD (only german version) and various Java development tools.
System Administration Responsible for the administration of the lab-computers is M. Baitsch (room
IA6/144). In the case that something is not working as expected or you need further software
to be installed, please contact us and we will assist as soon as possible.
Access to the Labs The labs in building IA can be used all day, except if there are lectures or exercises (see schedule on the doors to the labs). The lab in ICFW is only open during the lectures
or exercises.
Logging on You can log on at each of the computers (use logon to: CIP) using the account name
given to you. The account remains valid during your stay at the Ruhr-University of Bochum.
5
CHAPTER 1. OUR COMPUTER LABS
6
Your data The computers are configured such that your user profile is stored on a file server. This
means that your personal settings like desktop color and bookmarks are the same at each computer.
The folder where your own files (Java programs, own writing, drawings, . . . ) should reside is
called “My Documents”. This folder is redirected to the file-server and and therefore accessible
from each computer. Note 1: All data stored elsewhere is likely to be lost. Note 2: Disk space
in this folder limited to 300MB.
Printing and Scanning A black and white laser printer, a color inkjet printer and a scanner are in
room IA6/164. Bring your own paper for printing. Before you can use the printer, you have to
establish a connection to it (has to be done only once). Open “Printers and Faxes” (Startmenu),
click “Add a Printer”. After clicking “next” two times, a dialog appears, in which you choose
the radiobutton “Connect to this . . . ” and enter \\cipfs1\InnoLaser (cipfs1 is our file and
print server).
The computers are provided for your studies. This includes for using them in the exercises, doing
assignments, case studies, thesis and searching information. Writing private e-mail is no problem but
if all computers are occupied, their use for studying purposes has priority. Computer abuse, such as
downloading large amounts of data from the Internet results in complaints from the central computer
services and can forfeit your account, so don’t overdo it.
Responsibilities
Our responsibilities to you
Your responsibilities
• Keep the computers running
• Don’t misuse the computers
• Install necessary software
• Help to keep the rooms tidy
• Provide disk space for your files
• Turn off computer/monitor after use
• General support in using the computers
7
s
8
CHAPTER 1. OUR COMPUTER LABS
Chapter 2
Fundamental Programming Structures in
Java
This chapter is a guide to the fundamental programming structures in Java. It covers the Java way
of dealing with basic concepts present in most programming languages. While working your way
through this chapter, you will:
• use the programming environment Eclipse,
• write and run a minimalistic Java program,
• learn about the basic Java data types,
• use the most important operators,
• program and execute algorithms.
2.1 The Programming Environment Eclipse
Software development is an iterative procedure which is greatly facilitated by using a programming
environment (a program to develop programs). Throughout this course we will use the freely available
program Eclipse. Within Eclipse you can enter program codes as well as you can run and test your
programs. Figure 2.1 on the next page shows the toolbar of Eclipse.
In Eclipse Java program files are collected in projects which in turn reside in a workspace. Let us
now create a project for this chapter. Follow the steps below to create a project
1. Start Eclipse.
2. A workspace launcher dialog will pop up. Click OK and you will be greeted by a welcome
window. Just close it. You now should see the Eclipse workbench shown in Figure 2.1
3. From the menu select “File→New→Project”.
4. Select “Java Project”, then “Next”. The new project dialog pops up.
5. Fill in the “Project name” text box with the name “chapter 2” for this chapter and click “Finish”.
9
CHAPTER 2. FUNDAMENTAL PROGRAMMING STRUCTURES IN JAVA
10
run program
new package
new class
Figure 2.1: The Eclipse workbench
2.2 A Minimalistic Java Program
Since the fundamental book of Kerningham and Richie about the programming language C, nearly
every introduction to a programming language starts with a program writing out “Hello World”. We
will stick to this tradition by beginning with the following program:
1
public class HelloWorld {
2
public static void main(String[] args) {
System.out.println("Hello World");
}
3
4
5
6
}
Listing 2.1: Print “Hello World” to the Console
The following steps will guide you until you can run the program:
1. Create the class HelloWorld. To do so, click the corresponding button1 in the toolbar (see
Figure 2.1). The new class dialog pops up. Enter HelloWorld as the class name. Note that you
must not use blanks inside the name of a class (i.e. Hello World is not a valid name for a
class). Click OK to close the dialog.
1
Alternatively you could use the menu or the context menu of the project.
2.2. A MINIMALISTIC JAVA PROGRAM
11
2. Complete the program according to Listing 2.1. You may note that Eclipse makes some words
red and others blue. This is called syntax coloring and shall improve the readability of the
source code. If the code contains mistakes, the errors will be underlined with red lines.
3. Run your program. From the menu select “Run→Run as→Java Application” to run the program. If there are errors in your code, the error messages will be listed in the “Problems view”.
Double clicking an error message, the cursor jumps to the according place in your code.
2.2.1 Reviewing Hello World
It is worth spending all the time that you need to go through this program; the pieces will recur in all
applications.
• Java is case sensitive. If you type Main instead of main the program will not run. You may
want to try this out!
• In line 1, the keyword class followed by the identifier HelloWorld defines a new class
called HelloWorld2 . Because Java is strictly object-oriented, everything must happen inside a
class. The concept of classes will become clear in the next exercises; for now you may think of
a class as a container for the program logic that defines the behavior an an application.
The preceding keyword public is called access modifier; these modifiers control what other
parts of a program can use this code. For our purposes, we can state that all classes are public.
The opening curly brace { after the class name indicates the beginning of the class and the
closing curly brace } in line 6 its end. Everything inbetween these curly braces is said to belong
to the class.
• In line 3, the method main is declared. For the moment don’t worry neither about the keywords
public static void nor about the argument String[] args. The point to remember
for now is that each class you want to execute from within Eclipse must have a main method
whose header is identical to line 3.
As with the class in line 1, the body of the method is enclosed by a pair of curly braces. Whenever a method is called, each statement within its body is executed.
The class HelloWorld is rather untypical since it has just a single method main. Usually, a
program consists of many classes having many methods, whereby only one of the classes has a
main method.
• In line 4, the statement System.out.println("Hello World"); causes the string
“Hello World” to be printed in the “Console view”. At the moment, it is impossible for you to
understand how this works, just remember that this is the way how to print something.
Notice also, that the statement in line 4 is terminated by a semicolon as in Maple.
You may argue that this is a lot to type for such an easy thing. You’re right, but the strictly objectoriented design and the general applicability of Java imply the backdraw that simple things can look
complicated. On the other hand, this strictness of Java turns out to be an advantage when it comes to
complex software projects and this is what Java is made for.
2
A keyword is a reserved word which has a special, predefined meaning whereas identifiers are names chosen by the
programmer. A complete list of all Java keywords is in the last section of this chapter.
CHAPTER 2. FUNDAMENTAL PROGRAMMING STRUCTURES IN JAVA
12
2.3 Primitive Types
Java is a strongly typed language (other than e.g. FORTRAN). This means that every variable must
have a declared type. There are eight primitive types in Java which are used for “simple” things as
numbers, boolean values and characters. Table 2.1 contains a list of all Java primitive types. Throughout this course, you will only need the types shown in bold face in Table 2.1. The other types (apart
from char) are normally only used when storage space or computing time is very critical.
Type
Contains
Size
byte
short
int
long
float
double
boolean
char
integer
integer
integer
integer
floating-point
floating-point
boolean value
unicode character
8 bits
16 bits
32 bits
64 bits
32 bits
64 bits
1 bit
16 bits
Range
-128 . . . 127
-32768 . . . 32767
-2147483648 . . . 2147483647
-9223372036854775808 . . . 9223372036854775807
approximately ±3.40282347 · 1038
approximately ±1.79769313486231570 · 10308
true or false
ISO Unicode character set
Table 2.1: Java Primitive Types
Choosing an Adequate Type The data type you choose for a variable depends on the type of data
you want to store in it. As a little food for thought, insert after the arrow the data type you would use
to store
• the age of a person in years →
• the average age of persons in this room →
• the square root of two →
• whether you like German food →
• the number of working computers in this room →
2.4 Variables and Assignments
In Java, each variable has a type. You declare a variable by writing first the type and then the name of
the variable after the type. Here are some examples of variable declarations:
double temperature;
int hoursPerDay;
int daysPerWeek;
int hoursPerWeek;
boolean ready;
double x, y, z;
Note that you can also declare several variables of the same type in one line. After you declare a
variable, you must explicitly initialize it by means of an assignment statement.
2.5. OPERATORS
13
temperature = -9.3;
hoursPerDay = 24;
daysPerWeek = 7;
hoursPerWeek = hoursPerDay * daysPerWeek;
ready = true;
Be aware that an assignment is only possible if the expression on the right has an appropriate type.
For instance, it is not possible to assign a double value to an int variable. Thus, the assignment
hoursPerDay = temperature;
would cause an error during compilation. More general, an assignment is possible, if the value on
right hand side can be represented by the type on the left hand side without loss of precision. Because
any integer can be safely converted into a floating-point value, the assignment
temperature = hoursPerDay;
is completely legal. Also, is not possible to assign a number to a boolean variable. Use the literals
true and false in such situations.
A nice feature of Java is the ability to both declare and initialize a variable on the same line. There
are some examples:
int secondsPerMinute = 60;
int minutesPerHour = 60;
int secondsPerHour = secondsPerMinute * minutesPerHour;
Programming Assignment 1 Create a new class Time and add a main method as you did before.
In this first step, the program should compute and print out the number of minutes per week. The
output should look like
One week has N minutes
where N is replaced by the correct value. In order to get comfortable with the use of variables, please
stick to the scheme used above for the number of seconds per hour. The following code fragment
gives you an idea how to produce the desired output:
int minutesPerHour = 60;
System.out.println("An hour has " + minutesPerHour + " minutes");
The trick is to use the + operator to concatenate the individual entries to be printed out.
2.5 Operators
Arithmetic Operators The arithmetic operators +, -, *, / are used in Java for addition, subtraction, multiplication and division respectively. The / operator denotes integer division (the decimal
part is truncated) if both arguments are integers. Example:
double x = 1 / 4;
Now, x has a value of 0.0 because 1 and 4 both are interpreted as int. If one of the arguments is
not an integer, floating-point division is performed. Example:
double x = 1.0 / 4;
CHAPTER 2. FUNDAMENTAL PROGRAMMING STRUCTURES IN JAVA
14
Now, x has a value of 0.25 as expected because 1.0 is interpreted as a double. Unintended integer
division is a common source of errors.
The integer remainder (i.e. the mod function) is denoted by %. For example;
int rem = 15%2;
Now, rem has the value 1 because 15 divided by 2 is 7, remainder 1.
Increment and Decrement Operators The increment operator ++ adds 1 to a variable; the decrement operator -- subtracts 1 from a variable. Example:
int n = 10;
double x = 6.3;
n--;
x++;
Now, n has the value 9 and x the value 7.3.
Relational Operators The relational operators == != < <= > >= take numerical values and
evaluate to a boolean. To test for equality or inequality, use the == operator or the != operator,
respectively. For example, the value of
4.3 != 5
is true whereas
9%2 == 0
evaluates to false (this is actually a test whether 9 is an even number). The other relational operators
work similarly and their meaning should be self explaining.
Boolean Operators The two boolean operators && (logical and) and || (logical or) take two
boolean arguments and evaluate to a boolean. Examples: The value of
(5 < 6) && (8%2 == 0)
is true (both arguments are true) and the value of
(7%2 == 0) || (8 <=8 )
is also true because one of the arguments is true.
Complete List of Operators in Java For the sake of completeness, here is a list of all operators
defined in Java. Again, the operators you will need are in bold face.
=
==
+
+=
>
<=
-=
<
>=
*
*=
! ~
!= &&
/
&
/= &=
?
||
|
|=
:
++
ˆ
ˆ=
-%
%=
<<
<<=
>>
>>=
>>>
>>>=
2.6. METHODS AND CONTROL STRUCTURES
15
2.6 Methods and Control Structures
You already know Nassi-Shneiderman diagrams from the lecture. In this section, you will learn how
to translate a Nassi-Shneiderman diagram into Java.
2.6.1 Sequential Steps
The most fundamental element of a structure diagram is a single statement (step) which is represented
by a rectangle. Statements in the structure diagram can be often translated directly into Java. Note
that variables do not have to be declared in structure diagrams, so don’t forget to do so in Java.
x = 3.7
y = 5.9
z = 7.5 * x - y
Output z
double x, y, z;
x = 3.7;
y = 5.9;
z = 7.5 * x - y;
System.out.println("Value of z: " + z);
Figure 2.2: Example for a Sequence of Statements
One special statement in a structure diagram is Output. This translates into a print statement, preferably providing some information about the meaning of the output. In the lecture notes, you find an
example where Output is used to denote the result of an algorithm. This practice is not used in the
exercises (neither in the exam).
2.6.2 Method
Each algorithm takes one or more values as input and computes a result which is returned to the user
of the algorithm. This shall be illustrated by a very simple algorithm which just adds two integers.
add2(a: int, b: int): int
sum = a + b
return sum
public static int add2(int a, int b) {
int sum = a + b;
return sum;
}
Figure 2.3: Example for a Method
In the structure diagram on the left side, you find the name of the algorithm followed by the list of
the input values. Each input value has a name followed by its type. After the parameter list, separated
by a colon, is the type of the result. Return is another special statement in a structure diagram which
indicates that the following value should be returned to the user.
The Java equivalent starts with the keywords public static which you are kindly asked to
accept for the moment (but keep in mind that static methods are a special case rarely used in
general classes). The keyword int denotes the type of the return value of the method, then comes the
method name followed by the parameter list enclosed in parenthesis. The parameter list contains the
input values of the method having a type and a name. Note that the parameter list can also be empty.
Inside the method body, the keyword return hands the result of adding a and b back to the caller
of the method.
CHAPTER 2. FUNDAMENTAL PROGRAMMING STRUCTURES IN JAVA
16
The class MyMath Create a new class MyMath in Eclipse (it will contain some mathematical functions by the end of this chapter, therefore the name). Type in verbatim the Java code from the example
above. Note that this class will not contain a main method. When the file compiles create another
class called TestMyMath which you will use to test the methods in MyMath. Add a main method
to the class which looks as follows:
1
2
public static void main(String[] args) {
int r1;
3
r1 = MyMath.add2(3, 48);
System.out.println("MyMath.add2(3, 48) returns " + r1);
4
5
6
}
Listing 2.2: Testing the add2 method
Note that in line 4 you invoke the method add2 by using the class name MyMath followed by a dot
and the method name. The return value of the method is stored in the variable r1. Compile and run
the program, check that the result is correct.
Next, introduce a new method add3 to MyMath which takes three integers i1, i2, i3 as
input and returns the sum i1 + i2 + i3. Extend the main method of TestMyMath to invoke
the new method (you may want to use a new variable r2).
2.6.3 Selection
In a conditional statement (or a selection), a condition (which is either fulfilled or not) is used to
decide whether one sequence of statements is to be executed or another.
XXX
XX
condition X
T XXX F
block 1
block 2
if( condition ) {
block 1
}
else {
block 2
}
Figure 2.4: General form of a selection
In Java, a conditional statement starts with the keyword if followed by the condition enclosed
in parenthesis. The condition can be any expression evaluating to a boolean value. The block immediately after the condition is executed if the condition evaluates to true. After the first block you
see the keyword else which is followed by the block being executed otherwise. Note that the else
clause is optional.
2.6. METHODS AND CONTROL STRUCTURES
17
The following example determines the maximum of two double values:
max(a: double,b: double): double
XX
»
XXX a ≥ b
»»»
T
F
XXX»»»»
result = a
result = b
return result
public static double max(double a, double b) {
double result;
if (a >= b) {
result = a;
}
else {
result = b;
}
return result;
}
Figure 2.5: Taking the maximum of two doubles
In the lecture notes you find another branch-like construct called switch (or case differentiation).
It is not essential because any case differentiation can be constructed using a sequence of if and
else if directives.
Programming assignment Add a method isEven to the class MyMath that takes an integer value
as input and returns a boolean value indicating whether the argument is an even number. Test this new
method!
2.6.4 Indeterminate Loops
In Java, as in all programming languages, there are control structure that let you repeat statements.
There are two forms for repeating loops that are best when you do not know how many times a
loop should be processed (these are “indeterminate loops”). The while loop tests the continuation
condition at the beginning whereas the do/while loop test the continuation condition at the end.
Only the first form is introduced here.
continuation condition
block
while( continuation condition ) {
block
}
Figure 2.6: General form of a while-loop
CHAPTER 2. FUNDAMENTAL PROGRAMMING STRUCTURES IN JAVA
18
Indeterminate loops are often applied to algorithms which iteratively improve an approximate
solution. An example of such an algorithm is the bracketing algorithm to find the square root of a
positive real number.
public static double sqrt(double x) {
double low = 0;
double mid = 0;
double high = x;
sqrt(x: double) : double
low = 0
mid = 0
high = x
while (high - low > 1e-12) {
mid = 0.5 * (low + high);
high - low > 10−12
mid = 0.5 (low + high)
if (mid * mid > x) {
high = mid;
}
else {
low = mid;
}
mid2 > x
T
F
high = mid
low = mid
Return mid
}
return mid;
}
Figure 2.7: Bracketing the square root of a number (we assume that x is nonnegative)
Programming assignment
Implement and test the above algorithm.
Determinate Loops
Determinate loops are suited for situations when the number of iterations is known in advance. A
typical applications is to process each element of an array. The for loop is a very general construct
to support iteration that is controlled by a counter that is updated after each iteration.
initialization
continuation condition
update
block
for (init.; cont.; update) {
block
}
Figure 2.8: General form of a while-loop
Remark: Each for loop can be easily translated into a while loop (see Figure 2.9).
initialization
continuation condition
block
update
Figure 2.9: for-loop as while-loop
2.7. JAVA KEYWORDS
19
In the following example, a for loop is employed to compute the factorial n! of a nonnegative
integer n.
factorial(n: int): int
public static int factorial(int n) {
int fct = 1;
fct = 1
i=2
i≤n
i = i+1
for (int i = 2; i <= n; i++) {
fct = fct * i;
}
return fct;
fct = fct * i
Return fct
}
Figure 2.10: Compute the Factorial of a Positive Integer
Programming Assignment Add the above method to MyMath and test it for several input values.
Carefully study the results; do you trust them? What did eventually go wrong? Improve the method
by using another data type for the return value. Advanced: in the lecture notes you find something
about recursive method calls. Implement a method factorialRecursive that uses a recursive
algorithm.
Additional Programming Exercise In the lecture notes, you find three different algorithms to
compute the greatest common divisor of two whole numbers. Implement them inside of your class
MyMath. Don’t forget to test the methods.
2.7 Java Keywords
At the end of this section, a listing of all Java keywords is provided for completeness.
abstract
boolean
break
byte
case
catch
char
class
const
continue
default
do
double
else
extends
final
finally
float
for
goto
if
implements
import
instanceof
int
interface
long
native
new
package
private
protected
public
return
short
static
strictfp
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while
The keywords you should be familiar with after this semester are typed in boldface.
20
CHAPTER 2. FUNDAMENTAL PROGRAMMING STRUCTURES IN JAVA
Chapter 3
Objects and classes
This Chapter introduces the most fundamental concepts of object-oriented programming: objects and
classes. In Section 3.1 you will learn how to use existing classes, i.e. classes which have been already
programmed by somebody else. Next, in Section 3.2, you will learn how to program new classes
by yourself. In order to use object-type variables correctly, some theory is crucial. This is given in
Section 3.3 which is on computer memory, variables and objects. Sections 3.4 and 3.5 cover Java
strings and arrays. Finally, Section 3.6 clarifies the use of the static modifier which you have used a
lot in the previous chapter.
3.1 Using existing classes
In order to introduce object-oriented programming, a software package for 3D computer graphics
is employed. The package is called View3D and comprises a set of classes which represent threedimensional objects like cubes, spheres or arbitrarily shaped surfaces.
3.1.1 Three-dimensional objects
Step 1: Set up the project Before you can actually start, create a new project for this chapter and
name it ‘’‘chapter-3’. In order to be able to use the 3D graphics package, you have to adjust the
project settings. To make Eclipse aware of the 3D library, right click the project “‘chapter-3”’ and
choose “Properties” from the context menu. A dialog will show up. Choose “Java Build Path” from
the list to the left and select the “Libraries” tab. Click “Add Library...” and select the “ICEB MPCE”
entry. After clicking “Next” and “Finish” the project settings are complete.
21
CHAPTER 3. OBJECTS AND CLASSES
22
1
2
import inf.v3d.obj.*;
import inf.v3d.view.*;
3
4
public class MyScene {
5
public static void main(String[] args) {
Box box1 = new Box();
box1.setColor("red");
6
7
8
9
Viewer viewer = new Viewer();
viewer.setVisible(true);
10
11
}
12
13
}
Step 2: Create a red box object Create a new class MyScene and enter the code given above. Run
your program. A window displaying the red box shows up. Note that you can interact with the scene
using the mouse:
• Press the left mouse button and move the mouse to rotate.
• Press the right mouse button and move the mouse up and down in order to zoom in or out.
• Press the mouse wheel and move the mouse to pan.
What happens when you run the MyScene class?
Lines 1–2 If you want to use classes from the View3D library, you have to import the corresponding
packages.
Line 7 An object of type Box is constructed using the new operator and the box1 variable (of type
Box) points to the new object. In Java, objects are always constructed using the new operator
along with the type name.
Line 8 The color of the Box object, the box1 variable points to is changed to “red”. In Java, you
invoke a method on an object always using the variable pointing to the object along with the
method name and the “dot” operator.
Line 10 You create an object of type Viewer using the new operator. The variable viewer points
the the newly created object.
Line 11 The Viewer object, the viewer variable points to is set visible. This statement lets the
viewer show up on the screen.
3.1. USING EXISTING CLASSES
Step 3: Add more objects
method:
1
2
3
4
23
To add more objects to the scene, add the following lines to your main
Box box2 = new Box();
box2.setVertex(2.0, 0.0, 0.0);
box2.setSize(0.75, 0.5, 1.0);
box2.setColor("green");
5
6
7
8
Sphere sphere = new Sphere(1.5, 1.7, 0.5);
sphere.setRadius(0.2);
sphere.setColor("blue");
Note that these lines should be placed before the creation of the viewer. Run the program again.
Discussion: If you compare lines one and six, you’ll notice that that the statements differ. In line
one, a box object is created without passing any arguments. This means that the box is created with
its default properties. On the other hand, in line six, the sphere is constructed explicitly by specifying
the coordinates of the center. The method being called during object creation is called constructor and
that classes can define several constructors. You’ll find more about constructors later in this chapter.
Step 4: Read documentation and create a cone
1. Read the online documentation to get more information about the methods setVertex and
setSize of the Box class. To do so, put the cursor in the method name and press “shift + F2”
(sometimes, you will get an error message in the browser – just go back to eclipse and hit “shift
+ F2” again).
2. Add a yellow (or whatever color you like) cone such that your scene looks similar to the image
below. Make use of the online documentation of the Cone class!
CHAPTER 3. OBJECTS AND CLASSES
24
3.1.2 Another practical example: Plotting a function R2 → R
This example is about generating a three-dimensional plot of the function
f (φ, r) = e−r sin(πr)
which is defined using polar coordinates. The domain on which the function should be plotted is given
in Cartesian coordinates by −2.5 ≤ x ≤ 2.5 and −2.5 ≤ y ≤ 2.5.
In order to plot the function, you can use the class Mesh from the inf.v3d package. This class
represents a regular mesh of mxn points (see Figure 5.4). An individual mesh point is accessed by
its i, j-index in the grid (similar to the entries of a matrix). In order to find out more about the class,
consider the available documentation. One way to create the plot is shown as a structure diagram in
Figure 5.4. The algorithm takes as input the domain to plot (first four arguments) and the number of
points for each coordinate direction.
plot(xmin: double, xmax: double, ymin: double,
ymax: double, npx: int, npy: int): void
dx = (xmax - xmin) / (npx - 1)
dy = (ymax - ymin) / (npy - 1)
mesh = new Mesh(npx, npy)
bb = new BoundingBox(mesh)
i = 0; i < npx; i = i + 1
j = 0; j < npy; j = j + 1
x = xmin + i * dx
y = ymin + j * dy
z
y
x
r=
x2 + y2
z = e−r sin(πr)
A mesh consisting of 3 x 4 points.
mesh.setCoordinates(i, j, x, y, z)
mesh.setData(i, j, z)
mesh.createColors()
Figure 3.1: A Mesh (left) and the Algorithm to Plot the function (right)
In order to generate the plot, you can take the following steps as a guideline:
1. Create a new class PlotFunction.
2. Add a method plot to the class PlotFunction, choose the parameter list according to the
structure diagram and make it static. Note that the return type of the method is void.
Implement the algorithm to plot the function according to the above algorithm. The necessary
mathematical functions as well as the constant π are defined in the class Math available in Java
(examples: use Math.PI for π or Math.exp(x) to compute ex ). To find out more, you can
read the online documentation for the Math class.
3. Add a main method to your class PlotFunction. Inside this method, create the viewer,
call the plot function (reasonable values for the resolution are npx = 150 and npy = 150) and
finally set the viewer visible (similar to the example above).
3.2. PROGRAM YOUR OWN CLASSES
25
4. Run your program.
5. Modify your code and see how the output changes. For instance, plot the function on another
domain or change the number of grid points.
N OTE : The techniques employed to visualize the results of a finite element analysis are very similar
to what you did now. In fact, the library behind the view3D package is the Visualization Toolkit
(VTK), a C++ library for the visualization of huge scientific datasets. It is widely used e.g. in medical
imaging, geological sciences and computational fluid dynamics.
3.2 Program your own classes
3.2.1 A vector class
In this section, we will go into the details of designing and implementing a new class. We will do
this using of a class for vectors in R3 . This section is rather lengthy since many new concepts are
introduced. It is also thought to serve you as a reference, when you want to design and implement
your own classes.
The concept of vectors
You should all be familiar with the mathematical concept of vectors. Let us recall our knowledge in
order to get a clear idea of the functionalities our new class should provide.
Consider a vector in three-dimensional euclidean space. A vector x ∈ R3 is defined by
x = ei xi ,
where i ∈ {1, 2, 3}, ei are orthogonal unit vectors and xi are the components of the vector. Furthermore, Einstein’s summation convention is employed (the notation follows the lecture notes Finite
Element Methods in Linear Structural Mechanics by D. Kuhl and G. Meschke). Having this definition
of a vector, we can do several things:
1. define vectors
x = [3.2, 5.6, 8.1]T ,
y = [6.1, 4.1, 9.9]T ;
2. compute the scalar product of two vectors
α = x · y,
= xi yi ;
(remember the summation convention)
CHAPTER 3. OBJECTS AND CLASSES
26
3. multiply a vector by a scalar to get another vector
u = x 1.5;
4. of course, vectors can also be added and subtracted
x = y + u,
u = x − y;
5. and finally, we can compute the euclidean norm of a vector
α = kuk2 ,
√
u · u;
=
where the scalar product is used.
Having the above operations at hand, computations like
v = kuk2 (5.2 x + y − x · y u)
can be carried out.
Designing the class
Let us think about a vector class which stores the components of the vector and provides methods to
perform the above operations at the same time.
E XCURSION : T HE U NIFIED M ODELING L ANGUAGE (UML) Just like architects and engineers draw
plans and build models of a building before it is actually erected, programmers establish a model of
the software before they start programming. A software model describes the most relevant aspects of
a program without dealing with each detail (just like a good FE model of a whole bridge captures the
overall structural behavior without haggling for every bolt).
The Unified Modeling Language (UML) is a language to express software models. It defines
twelve different diagram types to describe various aspects of a program. We will now use a class
diagram to establish a model of our vector class.
In a class diagram, a class is represented by a box having three compartments: the first one holds
the class name, the second one the attributes and the third one is for the methods. We now go through
the three compartments of a class in UML notation by analyzing the above mathematical definition of
a vector in R3 .
Class Name The class name should briefly reflect the conceptual idea behind the class. For the vector
class the concept is a vector in 3D space and thus Vector3D is a fine name. Spending some
time thinking about a good name is always a good idea, since that helps to get a clear idea of
the class’s purpose and responsibility.
3.2. PROGRAM YOUR OWN CLASSES
27
Vector3D
-c1: double
-c2: double
-c3: double
+Vector3D(c1: double, c2: double, c3: double)
+print(): void
+scalarProduct(Vector3D: v): double
+multiply(alpha: double): Vector3D
+add(v: Vector3D): Vector3D
+subtract(v: Vector3D): Vector3D
+norm2(): double
N OTE : Java defines two other types of visibility in addition to public and private: protected
and default visibility. We will not use them in
the exercises. It is good practice to make all attributes private and most methods public (except
those only to be used internally). Non private attributes diminish encapsulation!
Figure 3.2: UML Class Diagram of the Vector Class
Attributes A vector in 3D is represented by its three components, one for each base vector. The
components are real numbers and therefore double is the right data type. We will store the
components in three individual attributes named c1, c2 and c3 as a shorthand form for component one, two and three1 .
Methods Deciding about the methods in a class is most important in the design process because
methods define the way how an object of this class interacts with other parts of the program.
Note, that in this stage of software development we are talking about the purpose of the method,
the arguments it takes and the result it produces. We do not decide how to implement the
method!
We’ll now discuss the operations defined mathematically in the steps 1–5 above.
1. In step 1, the vectors x and y are initialized. In Java the methods used to create and
initialize an object are called constructors. Constructors have the same name as the class,
can take any parameter list and have no return type (in that constructors are special). For
our purpose, it is convenient to define a constructor taking the three components of the
new vector as arguments.
2. In step 2, we take the vector x and ask it to compute the scalar product with y; the result
is a real number. Thus we equip the Vector3D class with a method scalarProduct
taking another Vector3D object as argument and returning a double.
3. In step 3, a new vector is created by multiplying x with a scalar value (i.e. a number). We
define a method multiply taking a double as argument and returning a new Vector
object.
4. The operations addition and subtraction in step 4 are similar. We call the methods add and
subtract; both take a Vector object as argument and return another, new Vector
object.
5. In order to compute the euclidean norm like in step 5, we define a method norm2 (for
brevity – the euclidean norm is also called two norm). In order to compute its euclidean
norm, a vector needs no arguments; the result is a double.
1
Using an array of doubles would be another option.
CHAPTER 3. OBJECTS AND CLASSES
28
Finally, we should introduce another method print to conveniently display a vector in the
console (the DOS window). It does not compute anything and therefore the return type is void
(which means nothing).
The class diagram expressing the above modeling decisions is shown in Figure 3.2. In the attribute
section, attributes are stated by their name and the data type – separated by a colon. The minus sign
at the beginning indicates that the attributes are private and are thus not visible from outside the class.
In the method compartment, you find the methods described by their name, their argument list and
the return type. The plus sign indicates that the method is public and thus usable in other parts of the
program.
In the lecture notes, you also find so called assertions and property strings along with methods:
we won’t use this feature of the UML in the exercises (neither in the exam).
Implementing the class
The last step is translate the above UML description into a Java definition and to test the implementation. We start with an incomplete definition of the class and then discuss the additional methods
subsequently. You find a complete listing of the class at the end of this section. Well, here is the
starting point for our class:
1
public class Vector3D {
2
private double c1_;
private double c2_;
private double c3_;
3
4
5
6
public Vector3D(double c1, double c2, double c3) {
c1_ = c1;
c2_ = c2;
c3_ = c3;
}
7
8
9
10
11
12
public void print() {
System.out.println("[" + c1_ + ", " + c2_ + ", " + c3_ + "]");
}
13
14
15
16
}
In line 1, you find the declaration of the class – you are already familiar with that. Lines 3 – 5 contain
the declaration of the attributes of the class. The clue about attributes is, that they can be used in any
method (except static ones) just like ordinary variables.
N OTE : The names of the attributes end with an underscore ( ). This is a coding convention we use in
this course for two reasons: i) the underscore allows you to clearly distinct attributes of the class from
local variables and ii) it prevents the hassle with shadowed variables. We strongly ask you to stick to
this convention. The underscore is not shown in UML notation.
The first method is the so called constructor. A constructor is a special method invoked whenever
you create an object via new. It has the same name as the class and no return type. In our case, the
3.2. PROGRAM YOUR OWN CLASSES
29
constructor takes three arguments that are used to initialize the individual components of the vector
(lines 8 – 10).
The print method is straightforward, here you just prints out the current values of the vector’s
components.
How To Read This Section In the following paragraphs, each method of the class is discussed in
detail. You may want to read each paragraph and then implement that method and test it. To do so,
create right now two new classes: Vector3D and Vector3DTest. Start with the incomplete class
as listed above and put the testing code in the main method of the test class. For the tests you can use
the mathematical statements at the beginning. If you want to, you can also take the output at the end
of this section as a guideline.
Scalar Product The scalar product x · y of the vectors x and y is mathematically defined as x · y =
x1 y1 + x2 y2 + x3 y3 . For implementing the corresponding Java method, you can think of being inside
the vector x (thus, the attributes c1 c2 c3 belong to x) and y is passed as a method argument.
The method implementation is
public double scalarProduct(Vector3D v) {
return c1_ * v.c1_ + c2_ * v.c2_ + c3_ * v.c3_;
}
Note, that you use the dot operator to access the components of the second operand. Go implement
and test the method. . .
Multiplication by a Scalar The multiplication of a vector by a scalar is performed component wise.
In the method, you first compute the components of the new vector, then you create it and finally, the
new vector is returned as the result of the method.
public Vector3D multiply(double alpha) {
Vector3D result;
double c1 = alpha * c1_;
double c2 = alpha * c2_;
double c3 = alpha * c3_;
result = new Vector3D(c1, c2, c3);
return result;
}
Addition and Subtraction The methods to add and subtract vectors are somewhat similar to the
multiplication by a scalar: first the components of the new vector are computed and then a new vector
is constructed and returned as result. Note that the way it is implemented here is equivalent to the
above, it just save two lines of code.
public Vector3D add(Vector3D v) {
double c1 = c1_ + v.c1_;
double c2 = c2_ + v.c2_;
double c3 = c3_ + v.c3_;
return new Vector3D(c1, c2, c3);
}
The subtract method is similar, implement it on your own.
CHAPTER 3. OBJECTS AND CLASSES
30
Euclidean Norm In order to compute the euclidean norm of a vector, you first compute the scalar
product of the vector with itself and then return the square root of that number. In Java, you use the
keyword this, if you want to refer to the object on which the method actually is invoked.
public double norm2() {
return Math.sqrt(scalarProduct(this));
}
3.2. PROGRAM YOUR OWN CLASSES
31
Complete Listing Finally, here is the complete listing of the class. Study the methods multiply,
add and subtract carefully. You will realize that they perform the same in general (compute
components of new vector and create it) with increasing compactness of the code.
public class Vector3D {
private double c1_;
private double c2_;
private double c3_;
public Vector3D(double c1, double c2, double c3) {
c1_ = c1;
c2_ = c2;
c3_ = c3;
}
public Vector3D
double c1 =
double c2 =
double c3 =
add(Vector3D v) {
c1_ + v.c1_;
c2_ + v.c2_;
c3_ + v.c3_;
return new Vector3D(c1, c2, c3);
}
public Vector3D multiply(double alpha) {
Vector3D result;
double c1 = alpha * c1_;
double c2 = alpha * c2_;
double c3 = alpha * c3_;
result = new Vector3D(c1, c2, c3);
return result;
}
public double norm2() {
return Math.sqrt(scalarProduct(this));
}
public void print() {
System.out.println("[" + c1_ + ", " + c2_ + ", " + c3_ + "]");
}
public double scalarProduct(Vector3D v) {
return c1_ * v.c1_ + c2_ * v.c2_ + c3_ * v.c3_;
}
public Vector3D subtract(Vector3D v) {
return new Vector3D(c1_ - v.c1_, c2_ - v.c2_, c3_ - v.c3_);
}
}
CHAPTER 3. OBJECTS AND CLASSES
32
Sample Output The output of the testing routine used for the reference implementation is listed
below. It is also a nice example for the effect of rounding errors. The last seven lines are of special interest: they test for the correctness of the implementation by checking if some mathematical
properties of vectors are fulfilled.
x = [3.2, 5.6, 8.1]
y = [6.1, 4.1, 9.9]
alpha = x * y
alpha = 122.66999999999999
u = x 1.5
u = [4.800000000000001, 8.399999999999999, 12.149999999999999]
x
x
u
u
=
=
=
=
y + u
[10.9, 12.499999999999998, 22.049999999999997]
x - y
[4.800000000000001, 8.399999999999999, 12.149999999999997]
alpha = |u|
alpha = 15.531339285457642
v = |u| (x 5.2 + y - u x * y)
v = [-24076.495784245013, -42767.00266840044, -61477.160579586794]
Test mathematical properties
Must be the null vector
v - v = [0.0, 0.0, 0.0]
v + v (-1) = [0.0, 0.0, 0.0]
The norm of a vector is a linear operator:
|v 4| = 314658.81050855166
4 |v| = 314658.81050855166
Compound Statements
To compute
x = [1, 2, 3]T
y = [2, 3, 4]T
z = 2.5 (x + y 1.5)
by using the Vector3D class, you can write
Vector3D x = new Vector3D(1, 2, 3);
Vector3D y = new Vector3D(2, 3, 4);
Vector3D z;
z = x.add(y.multiply(1.5)).multiply(2.5);
where you directly call a method for the result of another method call.
Conclusions
Now that you have implemented your first class, let us briefly review the individual steps:
1. We started from the mathematical concept of vectors and analyzed what kind of data we need
to represent a vector and which operations we can perform with vectors.
3.2. PROGRAM YOUR OWN CLASSES
33
2. In the design phase, we identified the vector components as relevant data of the class and decided to store them in three individual attributes, one for each component. Furthermore, we
decided about the methods by choosing the return type, the name of the method and the argument it takes. These design decisions have been expressed in a UML class diagram. It should be
emphasized that in this step, we only care about what the individual methods do (e.g. compute
the norm of a vector) and not how they do it.
3. In the implementation step, the class diagram has been translated into Java code. This is the
point, where we decided how to carry out the computations. Some of the methods generate a
new vector as result (the result of adding two vectors is a new vector). Inside these methods, we
therefore had to create a new Vector3D object which we then returned as the method result.
4. While testing the code, we created several instances of the Vector3D class by using the keyword new. Although all the objects belong to the class Vector3D, each has its own individual
values for the components
Although the Vector3D example is rather simple, it comprises all steps of object-oriented software
development: analysis, design and implementation.
3.2.2 Practical example: Complex numbers and the Mandelbrot set
In this practical example you will first implement a class for complex numbers and then employ this
class to plot a fractal set.
A class for complex numbers
Complex numbers are an extension to the concept of real numbers. Originally, complex numbers
have been introduced for the sake of a closed formulation of the problem to find the roots of algebraic equations. In structural engineering, complex numbers play a fundamental role in the theory of
vibrations.
Reminder The set
C = {z | z = α + i β; α, β ∈ R}
√
is called the set of complex numbers in which i denotes the imaginary unit defined by i = −1 i.e.
i2 = −1. For the complex number z = α + i β we call α the real part and β the imaginary part.
The following operations are defined for two complex numbers z1 = α1 + i β1 and z2 = α2 + i β2 :
1. Addition and subtraction
z1 ± z2 = (α1 ± α2 ) + i (β1 ± β2 )
2. Multiplication
z1 · z2 = (α1 α2 − β1 β2 ) + i (α1 β2 + α2 β1 )
3. Division
α2 β1 − α1 β2
α1 α2 + β1 β2
z1
+i
=
2
2
z2
α2 + β2
α22 + β22
In addition, we can compute the absolute value of a complex number:
|z| =
q
α2 + β 2
CHAPTER 3. OBJECTS AND CLASSES
34
Assignment 1
1. Complete the UML class diagram to contain the operations declared above. Note that we use
the term real for α and imag for β.
Complex
-real: double
-imag: double
+Complex(real: double, imag: double)
+print(): void
2. Implement the class in Java and write a test class in which you should test each implemented
method (for the moment, the method to compute the division can be omitted).
Plotting the Mandelbrot set
The Mandelbrot set has been introduced in 1975 by the mathematician Benoit Mandelbrot (born in
Poland in 1924).
The Mandelbrot set is defined as follows: Pick a point z0 in the complex plane and calculate
z1
=
z20 + z0
z2
=
z21 + z0
z3
=
z22 + z0
...
If the sequence z0 , z1 , z2 , z3 , . . . remains within a distance of 2 of the origin forever, then the point z0
is said to be in the Mandelbrot set. If the sequence diverges from the origin, then the point is not in
the set.
For practical reasons, the iteration is terminated after a finite number of steps or when the absolute
value is larger than 2. We can then define the Mandelbrot function, which returns either the number of steps until divergence occurs or the maximum number of steps ration between the number of
performed steps and the maximum number of steps or 1. The function value indicates how fast the
function diverges.
3.2. PROGRAM YOUR OWN CLASSES
35
Assignment 2
1. Create the class MandelbrotFunction and add the static method evaluate according to the following structure diagram.
evaluate(x: double, y: double): double
z0 = new Complex(x, y)
zn = new Complex(x, y)
m=0
m < 30 and zn.abs() < 2.0
zn = zn.multiply(zn).add(z0)
m=m+1
return m/30.0
2. Change your PlotFunction class from the last exercise such that it uses
MandelbrotFunction.evaluate(x, y) for the computation of z.
N OTE : The previous version of the class Mesh had a design flaw which caused the method
setData to be unnecessarily slow. This has been improved but you now must invoke
mesh.createColors() at the very end of your plot method. Make sure it is not inside
the loops since it is time consuming.
3. Plot the Mandelbrot function for −1.7 ≤ x ≤ 0.7 and −1.2 ≤ y ≤ 1.2. Start with a resolution
of npx = npy = 250. Your plot should roughly look like Figure 3.3 but nicer since you use a
higher resolution.
4. Increase the resolution to get a nicer image but keep in mind that the complexity of plotting the
function is O(npx*npy).
5. The fascinating thing about the Mandelbrot function is that its border is a complicated curve no
matter how close you look. Check this out by changing the plot range to 0.21 ≤ x ≤ 0.435
and 0.45 ≤ y ≤ 0.675. You might also experiment with different scaling factors for the z-value
and/or other plot ranges.
36
CHAPTER 3. OBJECTS AND CLASSES
Figure 3.3: Plot of the Mandelbrot function with a resolution of 500 in both directions.
3.3. COMPUTER MEMORY, VARIABLES AND OBJECTS
37
3.3 Computer memory, variables and objects
126
125
124
Computer memory On binary computers as we use them, information is encoded into a sequence
of so called bits (binary digits). A bit is either on (1) or off (0). Computer memory can be thought of
as a long strip of cells containing 0s or 1s. Eight bits are collected in a so called byte and each bite
has a memory address as shown in Figure 3.4.
... 1 1 0 1 0 0 0 1 0 1 0 1 0 0 1 1 1 0 1 0 ...










byte
byte
Figure 3.4: Computer memory
Today’s computers often have a memory size of one gigabite (1GB) which is 8 · 10243 = 8589934592
bytes. In Figure 3.4, one cell takes 3.5mm and for 1GB, the length of the strip would be
8589934592 · 3.5 · 10−6 = 30060km.

548
memory (raw)
variables


a

... 0 1 ...
0 1


...
1 0

...


... 1 1

124
Variables of different data types A variable can be thought of as a place in computer memory
where your program can store information. Figure 3.5 shows two variables a and b that both occupy
some amount of memory.
b
Figure 3.5: Variables
In Java, every variable has a data type that specifies how the sequence of bits is decoded. Java distinguishes between two different types of variables: primitive type variables and object type variables.
Both types have a specific meaning.
Primitive type variables In Java, there are eight basic data types like int. A complete listing of
primitive types is given in Table 2.1. For primitive type variables, we can say:
The content of a primitive type variable is the value.
Using the type information, the bit sequence for one variable can be decoded into a human readable
format. Figure 3.6 shows a piece of Java code and the corresponding memory state. From now on, we
will only use the decoded represention of computer memory.
32 bits
64 bits
321
0 1 1 ...
memory (raw)
321
124
124

...
1
memory (decoded)









x

0 0 0 ... 1 0 ...
3.0

program code
...








... 1 1 ...
double x = 3.0;
int a = 1;
a
Figure 3.6: Primitive type variables
variables
CHAPTER 3. OBJECTS AND CLASSES
38
...
double x = 3.0;
x = 3 * x;
...
2
542
1
542
Because primitive type variables store the value, assigning a value to a primitive type variable changes
the content of the variable’s memory. Figure 3.7 shows a code sequence and the corresponding memory state.
3.0 ...
at step 1
9.0 ...
at step 2



program code
x
Figure 3.7: Assigning to a primitive type variable
Object type variables An object can be thought of as a collection of data that resides somewhere
in memory. For object type variables, we can say:
The content of an object type variable is the address of an object.
2
1075
987



v1
v2
at step 1
1075
987
874
... #987 ... #987 ... vector obj. ... vector obj. ...



program code
... #987 ... #1075 ... vector obj. ... vector obj. ...
542
1
Vector3D v1 = new Vector3D(1,2,3);
Vector3D v2 = new Vector3D(3,2,1);
v2 = v1;
874
542
When an object is created via the operator new, the address is returned and can be stored in a variable.
When we assign a new address to an object type variable, the variable value changes to the new
address. This is illustrated in Figure 3.8 (we use the #-sign to indicate that a number represents an
address). First, we have two variables both pointing to two distinct boxes. After the assignment, both
variables point to one box and none to the other.
at step 2
variables
Figure 3.8: Object type variables
At this point, we introduce another notation for object variables. This new notation makes it easier to
visualize the state of an object and connections between objects. As shown in Figure 3.9, an object is
visualized as a box where the name of the object’s type is underlined. Variables are drawn below the
boxes and point to the boxes.
1
2
Vector3D v1 = new Vector3D(1,2,3);
Vector3D v2 = new Vector3D(3,2,1);
v2 = v1;
program code
Vector3D
Vector3D
Vector3D
Vector3D
c1 = 1
c2 = 2
c3 = 3
c1 = 3
c2 = 2
c3 = 1
c1 = 1
c2 = 2
c3 = 3
c1 = 3
c2 = 2
c3 = 1
b1
b2
b1
b2
state at step 1
Figure 3.9: New notation for object type variables
state at step 2
3.4. STRINGS IN JAVA
39
3.4 Strings in Java
Strings like ”Hello World” are represented in Java by a class called String, you already met this
class in every main-method you have implemented. This section briefly introduces Java strings. For
more information, consider the Javadoc for the String class.
The statement
String s1 = "Hello Bochum";
declares the variable s to be of type String and initializes it to “Hello Bochum”. Use the + operator
to concatenate strings
String s2 = s1 + ", how are you?";
You can combine each type of variable with a string, they automatically converted:
String s3 = Math.PI + " is roughly the value of pi";
The String class has lots of methods, for instance a method to extract substrings:
String s4 = s3.substring(0, 5);
String comparison Sometimes, you will need to check whether two strings are equal. In the previous chapter, you used the == to test primitive types for equality. You can’t do that for strings! For
object type variables, the operator == tests whether two variable refer to the same object and not
whether they have equal content (test for identity not equality). In order to compare two strings, the
String class provides the method equals that takes another string as an argument and returns a
boolean value. Example:
if(s2.equals(s3)) {
System.out.println("This should never happen");
}
if(!s1.equals("Hello Bochum")) {
System.out.println("Neither that");
}
3.5 Understanding Java arrays
Arrays are a fundamental data structure in each programming language. An array is a data structure
that stores a collection of values of the same type. You access each individual value through an integer
index specifying the position of the value in the array. Compared to C, C++ or FORTRAN, arrays in
Java are much easier to use:
• Java array know how large they are. You do not have to pass the length of the array to a function
as an extra argument.
• A bounds checking mechanism gives an error in the case of an out of bounds access to the array.
This greatly facilitates software development because bugs are much easier to find this way.
You declare an array variable by specifying the type of the values in the array followed by [] and
the name of the variable. For example, here is the declaration of an array of boolean values:
boolean[] b;
CHAPTER 3. OBJECTS AND CLASSES
40
However, this statement only declares the variable b to be an array of booleans. It does not yet
initialize it. Because arrays are some sort of objects in Java (they share a lot with objects, but differ in
some aspects), an array must also be created via new. The number of elements of the array is given
in brackets. For example, the statement
double[] x = new double[20];
creates an array of 20 doubles and stores it in the variable x. During array creation, the array elements
are initialized to their default values (which is zero for the numerical primitive types). Note, that the
difference to the creation of “normal” objects is that brackets [] are used instead of the parenthesis
(). Access to an element of an array looks like
x[0] = 44.2;
double y = x[19];
where the first statement stores a value in the array and the second reads a value from the array. Note
that array indexing is zero based in Java (other than in FORTRAN) and thus the statement
x[20] = 2.0;
results in an error message and program termination (remember that the array bounds are checked
during runtime).
To find the number of elements in an array named x, use x.length. A typical example is to
process each element of the array in a for loop:
for(int i = 0; i < x.length; i++) {
x[i] = 1.0 /(i + 1);
}
Arrays can not only be created for the Java primitive types as above, but also for object type variables
(remember that each class defines a type). Thus, the statement
String[] days = new String[7];
defines an array of strings. The values of object type arrays are initialized to null which indicates
that they do not refer to an object yet. Access to the array elements is similar as for primitive types:
days[0] = "Monday";
To be complete, it shall also be mentioned that two-dimensional arrays can be created in Java. The
statement
int[][] a = new int[30][10];
declares a matrix of 30 x 10 integers. To access the elements, two pairs of brackets must be used:
a[3][8] = 444;
Assignments In order to become comfortable with arrays, create a class ArrayTest, add a main
method2 and take the following points as a guideline:
1. enter the statements above
2. print the values of the array x
3. compute the sum of the entries of x and print it
4. make the array days complete and print it
2
From now on, we assume that you know that a main method is needed if you want to run a program.
3.6. STATIC METHODS AND CONSTANTS
41
The Parameter List of the Main-Method At the beginning of the course you might have wondered
about the String[] args in the argument list of the main method. Now you know that it is an
array of strings! The argument can be used to pass some information to the program at startup.
3.6 Static methods and constants
In the sample programs you have written, the main method is tagged with the static modifier.
Also, the methods in your MyMath class were static. We are now ready to discuss the meaning of this
keyword.
3.6.1 Static methods
In order to understand static methods, let us quickly recall the way “normal” methods work. In the
last chapter, you created a Box object and changed its color:
Box box1 = new Box();
box1.setColor("red");
In the first line, the new Box object is constructed and stored in the variable box1. Then, in the second
line you invoke the setColor method on your Box object that is stored in the variable box1. In
short: “normal” methods operate on objects that you have constructed using new.
In contrast, static methods are methods that do not operate on objects. You do not need to create
an object using new in order to invoke a static method. Remember the MyMath class from Chapter 3:
public class MyMath {
public static double add2(double a, double b) {
return a + b;
}
}
Because the add2 method is qualified with the static keyword, you can use the name of the class
to invoke add2:
double x = MyMath.add2(3.1, 4.5);
Another good example for the use of static methods is the Math class, you already know. Because
static methods belong to the class they are also called classifier methods. Normal methods, that operate
on instances of a class are often called instance methods.
When to use static methods? Sparingly. Static methods make sense for simple operations that only
need a few parameters. Good examples can be found in the Math class. Also, static methods are used
if only access to the static attributes of a class is needed.
3.6.2 Static attributes
If you define an attribute as static, that attribute only exists once. In contrast, “normal” attributes
exist for each object that you construct. Remember the several instances of the Vector3D class you
created: each of them had its own values for the components.
CHAPTER 3. OBJECTS AND CLASSES
42
Constants If you qualify an attribute as final, the value of this attribute can not be changed after
initialization. Often, static attributes are declared as final to define some predefined constant. An
example is Math.PI. The definition of π in the Math class is
...
public static final double PI = 3.14159265358979323846;
...
As you already did, you access a public static attribute by using the name of the containing
class and the dot operator:
double u = 4 * Math.PI;
On the other hand, you get an error during compilation if you would write
Math.PI = 4.0;
This is because PI is qualified as final in the Math class.
Finally, let us review the mysterious
System.out.println("Hello World");
System is a class that contains several useful static attributes and methods. One specific static attribute of the System class is out; it refers to an instance of the class PrintStream that represents
a stream on which you can write. Have a look at the Javadoc to find out more about print streams.
Chapter 4
Inheritance and Polymorphism
Chapter 3 introduced you to objects and classes. In this chapter, you will learn about inheritance,
another essential concept of the object-oriented programming paradigm. Basically, inheritance allows
you to build new classes upon existing classes. When you inherit from an existing class, you reuse
(or inherit) methods and attributes of the base class and you add new methods and fields to adapt your
class to a specific situation. Thus, it is also often said that a derived class extends the base class.
Closely related to inheritance is the concept of polymorphism that makes up much of the power of
object-oriented programming. Polymorphism means that variables of the base class type can refer to
any object of derived types whereby methods invoked on the base type are dispatched to the derived
type.
In this chapter you will:
• program classes that inherit from another class,
• define abstract classes,
• read about polymorphism,
• program classes for cross sections.
4.1 Extending Classes
Inheritance provides a formal mechanism for code reuse. Using inheritance, you build new classes
which add attributes and methods to an existing class. We will do that in order to represent the
employees of a company in a Java program.
Every employee of a company has a name. In addition, special kinds of employees exist, for
instance, an employee can be a programmer using a specific programming language or a sales manager
who is responsible for a certain number of clients.
In order to establish a class model of the above situation, we first identify suitable classes and
then the relations amongst the classes. For the classes, let us use Employee, Programmer and
SalesManager. Since both programmers and sales managers are special kinds of employees, the
classes Programmer and SalesManager inherit from the class Employee. Let us also introduce
a method doWork that prints out what programmers and sales managers are doing at work time. The
corresponding UML class diagram is shown in Figure 4.1 (next page). Remember that we use the
triangle pointing to the base class to indicate inheritance in a UML class-diagram.
43
CHAPTER 4. INHERITANCE AND POLYMORPHISM
44
Employee
-name: String
+setName(name: String): void
+getName(): String
SalesManager
Programmer
-language: String
-clients: int
+setLanguage(lang: String): void
+getLanguage(): String
+doWork(): void
+setClients(clients: int): void
+getClients(): int
+doWork(): void
Figure 4.1: UML Class Diagram for Employees
How To Read This Section It is suggested, that you program the classes while reading the text. In
order to keep your files organized, you may want to create a new project “chapter5” in eclipse.
The Base Class
The implementation of the Employee is straightforward:
public class Employee {
private String name_;
public void setName(String name) {
name_ = name;
}
public String getName() {
return name_;
}
}
N OTE : In the class diagram (and of course in the Java code), you find for each attribute of the classes
a pair of set and get methods. Using such pairs of set and get methods to modify and access the
attributes of an object is common practice in Java programming.
4.1. EXTENDING CLASSES
45
The Inheriting Classes
Let us start with the Programmer class:
public class Programmer extends Employee {
private String language_;
public void setLanguage(String language) {
language_ = language;
}
public void doWork() {
System.out.println("Programs in " + language_ + ".");
System.out.println("Drinks a cup of coffee.");
}
}
In the first line, the keyword extends indicates that the Programmer class adds something to
(and thus extends) the Employee class. The other way round, the Programmer class inherits
everything from the Employee class. Inside the Programmer class the additional features are then
implemented.
The SalesManager class is very similar:
public class SalesManager extends Employee {
private int clients_;
public void setClients(int clients) {
clients_ = clients;
}
public void doWork() {
System.out.println("Makes a phone call to one of his " + clients_
+ " clients.");
System.out.println("Checks the sales of the last month.");
}
}
CHAPTER 4. INHERITANCE AND POLYMORPHISM
46
Using the Derived Classes
In order to find out how derived classes can be used, let us introduce a EmployeeTest class:
1
public class EmployeeTest {
2
public static void main(String[] args) {
3
4
Programmer p1 = new Programmer();
p1.setName("Valery Miller");
p1.setLanguage("FORTRAN");
5
6
7
8
SalesManager sm1 = new SalesManager();
sm1.setName("John Wood");
sm1.setClients(16);
9
10
11
12
// the programmer
System.out.println(p1.getName() + " is working:");
p1.doWork();
13
14
15
16
// the sales manager
System.out.println(sm1.getName() + " is working:");
sm1.doWork();
17
18
19
}
20
21
}
In line 7, we declare and a variable p1 of type Programmer and construct a new Programmer
object. In line 8, we give our employee a name. But wait! The method setName is not implemented
in the class Programmer, so how can we invoke it?. We can, because the Programmer class
inherits from the class Employee which has a method setName. In the lines 11–13, a sales manager
is constructed and initialized. In the following lines, we print out what the employees are doing during
work time.
4.1.1 Using the Base Class Instead of the Derived Class
An important issue when dealing with derived classes is that you can use the type of the base class to
store an object of the derived class. Thus, the last two lines in
Programmer p1 = new Programmer();
p1.setName("Valery Miller");
p1.setLanguage("FORTRAN");
Employee e1 = p1;
System.out.println(e1.getName());
are perfectly legal because each programmer is also an employee.
For the moment, the advantage of using the base type instead of the derived type may not be
obvious. But together with polymorphism it is a concept that is absolutely essential for efficient
object-oriented programming.
4.2. ABSTRACT CLASSES
47
4.2 Abstract Classes
Suppose we want to use the above classes to represent the employees of a larger company. A company
may employ many programmers and sales managers. Therefore it would be very convenient to manage
the employees in a unified manner instead of treating programmers and sales manager differently (as
we did above).
For instance, we could think of storing the employees in an array and then use a for loop to print
out what the employees are doing. In order to do so, we can change the EmployeeTest class so
that it looks like:
1
package step2;
2
3
public class EmployeeTest {
4
public static void main(String[] args) {
5
6
Programmer p1 = new Programmer();
p1.setName("Valery Miller");
p1.setLanguage("FORTRAN");
7
8
9
10
SalesManager sm1 = new SalesManager();
sm1.setName("John Wood");
sm1.setClients(16);
11
12
13
14
Employee[] employees = new Employee[2];
employees[0] = p1;
employees[1] = sm1;
15
16
17
18
for (int i = 0; i < employees.length; i++) {
System.out.println(employees[i].getName() + " is working:");
employees[i].doWork();
System.out.println();
}
19
20
21
22
23
}
24
25
}
In contrast to the previous version, in the lines 13–15 we create an array of two employees and store
the programmer and the sales manager in that array. Since both variables p1 and sm1 are of type
Employee we can do that. Next, we use a loop to process each employee. Line 18 is fine because
getName is defined in the Employee class. But the compiler will complain about line 19 because
the Employee class does not have a method doWork (try this out).
On the other hand, the approach makes sense, since all employees are working; we already implemented the methods. What we have to do is to introduce a doWork method in the Employee class.
But how should we? We can not know in the Employee class what a programmer or a sales manager
does during work.
The solution is to introduce the doWork method in the Employee class and to declare it as
abstract. An abstract method only specifies that an object of that type does have the method but does
not implement it. The implementation of the method is delegated to the derived classes. A class
containing abstract methods is itself also called abstract. You can not create objects of an abstract
class! Let us now change the Employee class such that the above program works.
CHAPTER 4. INHERITANCE AND POLYMORPHISM
48
Employee
-name: String
+setName(name: String): void
+getName(): String
+doWork(): void
printed version (uses italic font)
sketch (uses {abstract})
Figure 4.2: The Improved Employee Class
Figure 4.2 shows the improved version of the Employee class in a UML class diagram. In addition
to the previous version, we now have a doWork method which is abstract. In the class diagram
this is indicated by either using an italic font or the annotation {abstract} in the hand-drawn version.
Because the Employee class contains abstract methods it is also marked as abstract. Note that no
changes have to be introduced in the Programmer and the SalesManager classes since they
already have a doWork method.
In Java, you use the keyword abstract to qualify both classes and methods as being abstract:
package step1;
public class Employee {
private String name_;
public void setName(String name) {
name_ = name;
}
public String getName() {
return name_;
}
}
Note that the doWork method does not have a method body but is just completed with a semicolon
after the parameter list (which is empty in this case).
After you have changed the Employee class, the compiler should be willing to compile the
EmployeeTest class. The output of the program should look something like
Valery Miller is working:
Programs in FORTRAN.
Drinks a cup of coffee.
John Wood is working:
Makes a phone call to one of his 16 clients.
Checks the sales of the last month.
The program output makes clear that in Java object variables are polymorphic. This is discussed in
the next section.
4.2. ABSTRACT CLASSES
49
4.2.1 Polymorphism
In order to discuss polymorphism, let us revisit the code doing the output:
1
2
3
Employee[] employees = new Employee[2];
employees[0] = p1;
employees[1] = sm1;
4
9
In the lines 1–3, we store the Programmer object and the SalesManager object in an array of
the type Employee. A variable of type Employee can refer to an object of type Employee or
any subclass of the Employee type (such as Programmer or SalesManager). In line 7, you
invoke the doWork method on an object of type Employee (as you declared in line 1). On the other
hand, you can observe – when studying the program output – that nevertheless the correct method
implemented in the derived class is invoked. This behavior is called polymorphism.
Despite the fact that polymorphism is relatively hard to understand (and also to explain) in theory,
it is rather intuitive to use.
4.2.2 The Three Pillars of Object-Oriented Programming
In the previous chapter, you learned about encapsulation. In this chapter, you used inheritance and
polymorphism. These three concepts are the most fundamental ones to object-oriented programming.
Some textbooks use also the term “the three pillars of object-oriented programming” in this context.
Figure 4.3 illustrates them.
Polymorphism
8
Inheritance
7
for (int i = 0; i < employees.length; i++) {
System.out.println(employees[i].getName() + " is working:");
employees[i].doWork();
System.out.println();
}
Encapsulation
5
6
Figure 4.3: The Three Pillars of Object-Oriented Programming
4.2.3 Abstract Classes in Finite Element Programming
In the context of the Finite Element method, there are plenty of candidates for abstract classes:
Material models: A material model calculates for a given strain tensor the corresponding stress tensor. Conceptually, this computation is the same for a simple isotropic elastic material model as
for a complex plastic material model. By introducing an abstract class MaterialModel all
specific material models could be used in the same manner. Polymorphism delegates the stress
calculation to the concrete class (e.g. IsotropicElasticMM) as needed.
CHAPTER 4. INHERITANCE AND POLYMORPHISM
50
Element formulations: An abstract class Element declares abstract methods to compute the element stiffness matrix ke and the element load vector re . The concrete subclasses (e.g. Truss3D)
are then responsible for these computations.
Cross sections: One dimensional elements need to know the properties of the cross section such as
the area or the moments of inertia. An abstract class CrossSection declares the corresponding methods which are implemented in the concrete subclasses like RectangularCS.
The mechanism of polymorphism has a very important property: it makes programs extensible. For
instance, in the context of Finite Element programming you could introduce a new material model
without having to change any code inside the element classes that use a material model. The element
only has to know that the material model computes stresses. It does not have to know anything about
how this is done.
4.3 Practical Example: Sections
In order to establish a model of a structure consisting of one-dimensional members, we need classes
that represent cross sections. For our purposes, a cross section should be able to compute the area and
it should provide the vertexes of its outline for visualization. The outline is defined in two-dimensional
space.
The functionality described above should be declared in an abstract class CrossSection.
Starting Point
As a starting point, here is a class for rectangular cross sections:
public class RectangularCS extends CrossSection {
private double width_;
private double height_;
public RectangularCS(double width, double height) {
width_ = width;
height_ = height;
}
public double getArea() {
return width_ * height_;
}
public double[][] getOutline() {
double[][] outline = new double[4][2];
outline[0][0]
outline[0][1]
outline[1][0]
outline[1][1]
outline[2][0]
outline[2][1]
outline[3][0]
outline[3][1]
=
=
=
=
=
=
=
=
-width_ / 2.0;
-height_ / 2.0;
width_ / 2.0;
-height_ / 2.0;
width_ / 2.0;
height_ / 2.0;
-width_ / 2.0;
height_ / 2.0;
4.3. PRACTICAL EXAMPLE: SECTIONS
51
return outline;
}
public double getHeight() {
return height_;
}
public double getWidth() {
return width_;
}
}
Be aware that this class needs the abstract class CrossSection to compile properly.
Assignments
• Sketch a class diagram containing at least four different types of cross sections. Identify the
relevant parameters for those classes (e.g. width and height for a rectangular cross section).
• Implement in Java the abstract base class CrossSection and at least one concrete class in
addition to RectangularCS that extends CrossSection. Hint: a circular outline can be
approximated by a polygon.
• Create another class to test your classes. Create different sections and compute the area.
• Use the Extrusion class from the inf.v3d package to visualize a simple structure. Look
at the Javadoc of the Extrusion class to find out how to use it. Note that you can use the
outline of one section for several Extrusion objects:
RectangularCS cs1 = new RectangularCS(0.1, 0.1);
Extrusion e1 = new Extrusion();
e1.setOutline(cs1.getOutline());
e1.setPoint1(0,0,0);
e1.setPoint2(0,1,0);
Extrusion e2 = new Extrusion();
e2.setOutline(cs1.getOutline());
e2.setPoint1(3,0,0);
e2.setPoint2(3,1,0);
The figure shows an example that uses I-shaped cross sections.
52
CHAPTER 4. INHERITANCE AND POLYMORPHISM
4.4. INTERFACES
53
4.4 Interfaces
In Java, interfaces provide a way to specify a set of methods without giving any implementation. A
class that provides the methods specified in an interface is said to implement that interface. In that
sense, interfaces are similar to classes that have only abstract methods. The difference is that a class
can only have one superclass but it can implement any number of interfaces.
Figure 4.4 shows a UML class diagram that contains two interfaces and a class that implements
both interfaces. Note that we can use two different notations to express that a class implements an
interface: One inheritance-like style and one lollypop-style.
<<interface>>
PublicTransport
+getCapacity(): int
<<interface>>
MotorVehicle
+getEnginePower(): int
MotorBus
MotorBus
PublicTransport
MotorVehicle
Alternative notation
Figure 4.4: Some sorts of vehicles
In Java, interfaces are declared using the interface keyword.
public interface PublicTransport {
public int getCapacity();
}
public interface MotorVehicle {
public double getEnginePower();
}
Use the implements keyword to indicate that a class implements an interface.
public class MotorBus implements PublicTransport, MotorVehicle {
public int getCapacity() {
return 30;
}
public double getEnginePower() {
public double return 154.2;
}
}
54
CHAPTER 4. INHERITANCE AND POLYMORPHISM
The important issue about interfaces is that each interface defines a datatype that can be used to
declare variables. Consider for instance the following code fragment. It contains two methods that
take objects of type PublicTransport or MotorVehicle resp. as arguments.
public void printCapacity(PublicTransport p) {
System.out.println(p.getCapacity());
}
public void printEnginePower(MotorVehicle m) {
System.out.println(m.getEnginePower());
}
The most difficult thing about interfaces is to explain why we need them. For me, the most
important issue is that interfaces introduce a higher level of abstraction to a software project that
improves software quality. In some sense, interfaces isolate areas of high complexity and thus help to
split a software in smaller independent units.
Chapter 5
Connecting Objects
Chapter 4 was about inheritance which is a relationship amongst classes where one class adds something to an existing class. This chapter is about relations between objects that are established during
the runtime of a program: associations, aggregation and composition. While inheritance is directly
supported by the Java programming language (remember the keyword extends) there is no direct
language support for associations, aggregation and composition: You program these types of relations
by means of attributes. Since programming associations is best learned by example, in this chapter
you will program
• a catalog for a library (simple example);
• classes to represent structures consisting of truss elements (more complex example).
5.1 Programming Associations / Aggregation / Composition
Object-oriented programs consist of many cooperating classes. Thus, the classes must be related. The
UML defines three different types of relationships:
Associations represent relationships between instances of classes (a person works for a company;
a pedestrian walks on a bridge; a bridge has several pedestrians walking on it). In a UML
class diagram, an association is represented by a line connecting the two classes involved in the
association.
Aggregation is the part-of relationship. It’s like saying that a personal computer consists of a main
board, a CPU, a hard disk and so on. In UML, aggregation is indicated by a line connecting the
involved classes where the line has a diamond at the end of the aggregated class.
Composition is a stronger form of aggregation. With composition, the part object may belong to
only one whole; further the lifetime of the parts is usually the same as the lifetime of the whole.
In UML, you draw composition like aggregation but the diamond is black.
It is crucial to understand that the different types of relationships defined by the UML are used to specify the meaning of a relation (its semantics); from an implementation point of view, this distinction is
less important: All these kinds of relationships are programmed by means of attributes.
In UML, associations are the most general type of relations; aggregation and composition are
more specialized types of associations. Therefore, in the following often the term association is used
collectively for association, aggregation and composition.
55
CHAPTER 5. CONNECTING OBJECTS
56
For the implementation, multiplicities (i.e. the number of objects involved in an association) are
much more important than the specific type of the association: Multiplicities determine the type of
attribute you use the program the association. As a rule of thumb, you can use the following scheme:
• Use a simple attribute to program an association with an individual object. Example: the author
of a book.
• Use an array for an association involving several objects when the number of objects does not
change during the runtime of the program. Example: the seats in an airplane.
• Use a resizable collection (e.g. java.util.ArrayList) for an association if the number
of the involved objects changes during runtime. Example: the books in a library catalog.
Please note, that this scheme is rather coarse and that the choice of an approach depends highly on the
specific situation you have to deal with.
Because of the wide variety of ways to program associations, it doesn’t seem possible to give a
systematic introduction to this topic. Rather, the subject is discussed with two examples: Section 5.2
is about a catalog of books and Section 5.3 about structures consisting of truss elements.
5.2 Catalog of Books for a Library
This simple example is about programming a catalog that stores the books of a library. The situation
to program is the following:
The catalog of a library consists of entries for each book the library contains. Over time
new books are added to the catalog and other books (that got lost) are removed from the
catalog. Each book has an author whereby we do not consider the situation where a book
has more than one author.
In order to write the program, first the UML class diagram shown in Figure 5.1 is established. The
Catalog class is associated with an unspecified number of books (you could also argue, that the
catalog consists of books and we should use aggregation). Furthermore, the method addBook adds
the specified book to the catalog, the method removeBook removes the specified book from the
catalog and the method print prints the content of the catalog. The Book class has an attribute for
the title and an association with the person who wrote the book, i.e. the author. Both properties, the
author and the title are passed to the constructor of the class. The Person class is straightforward
and needs no explanation. Of course, the author could also be represented by an attribute of type
String, but using a special class is more flexible. For instance, it could become necessary to also
store the date of birth of the author.
Catalog
+addBook(b: Book): void
+removeBook(b: Book): void
+print(): void
books
0..*
Book
-title: String
+Book(author: Person, title: String)
+getTitle(): String
+getAuthor(): Person
Figure 5.1: UML class diagram
author
1
Person
-name: String
+Person(name: String)
+getName(): String
5.2. CATALOG OF BOOKS FOR A LIBRARY
57
5.2.1 Java Implementation of the Classes
In the following, one possible implementation of the above class diagram is presented. In order to get
a better understanding, you may want to program the classes along with reading the text.
The Person Class
The Person class does not need any explanation.
1
public class Person {
2
private String name_;
3
4
public Person(String name) {
name_ = name;
}
5
6
7
8
public String getName() {
return name_;
}
9
10
11
12
}
The Book Class
In the Book class the association to the author is represented by the attribute author of type
Person. Both attributes, the author and the title of the book are initialized in the constructor of
the Book class.
1
public class Book {
2
private Person author_;
private String title_;
3
4
5
public Book(Person author, String title) {
author_ = author;
title_ = title;
}
6
7
8
9
10
public Person getAuthor() {
return author_;
}
11
12
13
14
public String getTitle() {
return title_;
}
15
16
17
18
}
N OTE : Although the association with the Person class is represented by the attribute author that
attribute does not appear in the UML diagram of the Book class. This is common practice: Attributes
representing associations are usually not listed in the attribute section of the class.
CHAPTER 5. CONNECTING OBJECTS
58
The Catalog Class
The Catalog class is the most complicated one because we want to be able to add and remove books
from the catalog. For such purposes, Java comes with the so-called collections framework. One class
from this framework that is well suited for our purpose is the ArrayList class. Note that the classes
from the collections framework are in the package java.util which must therefore be imported
(Line 1).
The association to the books is implemented by the attribute books which is of type ArrayList.
Note that in the corresponding Line 5, the attribute is also initialized (i.e. an ArrayList object is
constructed).
The methods to add and remove a book are straightforward, we just add or remove the specified
book to/from our list of books (look at the Javadoc of the ArrayList class for information about
the methods add and remove).
In the print method, each book stored in this catalog is processed. To find out the number of
objects stored in an ArrayList, this class provides the method getSize. In order to retrieve an
object from the collection, we can use the get method. The trick is to perform the explicit type
conversion (Book) in Line 17 because the get method returns an object of type Object and not of
type Book. In Java, the Object class is the base class of any any class that is not explicitly derived
from another class.
1
import java.util.*;
2
3
public class Catalog {
4
private ArrayList books_ = new ArrayList();
5
6
public void addBook(Book b) {
books_.add(b);
}
7
8
9
10
public void removeBook(Book b) {
books_.remove(b);
}
11
12
13
14
public void print() {
for (int i = 0; i < books_.size(); i++) {
Book b = (Book) books_.get(i);
15
16
17
18
System.out.println(b.getTitle() + " by " + b.getAuthor().getName());
19
}
20
}
21
22
}
N OTE : You can use the code in the Catalog class as a template if you have to deal with resizable
collections. Of course, you have to adequately replace the class name Book in the explicit type
conversion (Line 17).
5.3. TRUSS STRUCTURES
59
Testing the classes
You can use the following code to test the classes.
1
public class CatalogTest {
2
public static void main(String[] args) {
Catalog catalog = new Catalog();
3
4
5
Person author1 =
Book book1 = new
Book book2 = new
Person author2 =
Book book3 = new
Person author3 =
Book book4 = new
6
7
8
9
10
11
12
new Person("Herman Melville");
Book(author1, "Moby Dick");
Book(author1, "Pierre");
new Person("Sylvia Plath");
Book(author2, "The Bell Jar");
new Person("Isaac B. Singer");
Book(author3, "Short Friday and Other Stories");
13
catalog.addBook(book1);
catalog.addBook(book2);
catalog.addBook(book3);
catalog.addBook(book4);
14
15
16
17
18
System.out.println("The catalog contains:");
catalog.print();
19
20
21
System.out.println("Removed one book from the catalog:");
catalog.removeBook(book2);
catalog.print();
22
23
24
}
25
26
}
5.3 Truss Structures
This section is about developing a collection of classes to represent structures consisting of truss
elements. By using the classes, it should be possible to define a structure of truss elements to calculate
the volume and to draw the structure in 3D. Figure 5.2 shows an instance of such a structure.
Figure 5.2: Curved Girder
CHAPTER 5. CONNECTING OBJECTS
60
5.3.1 Designing the classes
In order to design the classes, let’s start with a description of a truss structure:
A truss structure consists of cross sections, nodes and elements. Each element has a cross
section and two nodes. There are different types of cross sections: rectangular cross
sections, circular cross sections and so on.
Based upon this description, the class diagram in Figure 5.3 has been created. Note that description
and the class diagram are closely related.
TrussStructure
+addNode(x : double, y : double, z : double) : void
+addCrossSection(cs : CrossSection) : void
+addElement(idxN1 : int, idxN2 : int, idxCS : int) : void
+draw() : void
+getVolume() : double
0..*
elements
Element
+Element(n1 : Node, n2 : Node, cs : CrossSection)
+getVolume() : double
+draw() : void
0..*
crossSections
1
crossSection
2
nodes
CrossSection
0..*
nodes
Node
+getArea() : double
+getOutline() : double[][]
-x : double
-y : double
-z : double
+Node(x : double, y : double, z : double)
+distance(n : Node) : double
+getX() : double
+getY() : double
+getZ() : double
RectangularCS
...
Figure 5.3: UML Class Diagram
5.3. TRUSS STRUCTURES
61
5.3.2 Assignment: Implementing the Classes in Java
It is now up to you to implement the classes in Figure 5.3 in Java. Of course it is intended, that you
reuse the classes for the cross sections you programmed in the last chapter. Take the following steps
as a guideline.
Step 1: Preliminaries
In order to use the existing cross section classes, you have to adjust the settings for your current project
in eclipse. In order to do so, right click your current project and choose “Properties”. In the properties
dialog select “Java Build Path” on the left and then the “Projects” tab on the right. Click the “Add...”
button and select your project which contains the sections.
Next, create a new class TrussStructureTest and add a main-method. This is the place
where your testing code goes.
Step 2: The Node Class
The Node class is very similar to the Vector3D class – implement it on your own.
You can use the following code fragment (put it in the main-method of your TrussStructureTest
class) to test your Node class:
Node n1 = new Node(0, 0, 0);
Node n2 = new Node(1, 2, 3);
System.out.println("Distance: " + n2.distance(n1));
System.out.print("n2: ");
System.out.println(n2.getX() + ", " + n2.getY() + ", " + n2.getZ());
The output should look like
Distance: 3.7416573867739413
n2: 1.0, 2.0, 3.0
Of course, you can also implement all the classes first and then start to test them. But generally,
testing the classes individually and early makes it easier to find errors, especially if the program
is more complex. By the way, in professional software projects testing takes as much time as the
programming part itself.
Step 3: The Element Class
The Element class is more complex. You will need attributes to represent the associations with the
CrossSection object and the two Node objects. Decide on your own, whether you want to use
an attribute nodes of type Node[] or two attributes node1 and node2 for the association with
the two nodes of the element. The attributes are initialized in the constructor. You use the attributes
in the getVolume method to calculate the volume of the element. In the draw method, you create
an Extrusion object for the graphical representation (import the inf.view3d package). The
following code is an example of how the Element class can be used.
Viewer viewer = new Viewer();
Node n1 = new Node(0, 0, 0);
Node n2 = new Node(1, 0, 1);
RectangularCS cs = new RectangularCS(0.1, 0.2);
Element e1 = new Element(n1, n2, cs);
CHAPTER 5. CONNECTING OBJECTS
62
System.out.println("Volume: " + e1.getVolume());
e1.draw();
viewer.setVisible(true);
Step 4: The TrussStructure Class
In the TrussStructure class, you can use three attributes of the type ArrayList to program
the relationships with nodes, cross sections and elements (have a look at the Catalog class for a
reminder).
The method addCrossSection works similar to the addBook method of the Catalog class.
In the addNode method, you first create a new Node object according to the specified coordinates
and then add it to the ArrayList attribute for the nodes. The addElement method takes the
indexes of the two nodes and the index of the cross section as arguments. Here, the index corresponds
to the sequence of addition, i.e. the first node that was added has the index 0. Inside the addElement
method, you first retrieve the nodes and the cross section (as specified by the indexes in the parameter
list) and store them in local variables. Use the get method of the ArrayList class and don’t forget
the explicit type conversion. Second, you create the new Element object which is finally added to
the list of elements. The methods draw and getVolume involve a loop over all elements.
This code fragment shows, how the TrussStructure class can be used:
Viewer viewer = new Viewer();
TrussStructure ts = new TrussStructure();
CircularCS ccs = new CircularCS(0.01);
IShapedCS iscs = new IShapedCS(0.05, 0.1, 0.001, 0.005);
ts.addCrossSection(iscs);
ts.addCrossSection(ccs);
ts.addNode(0.0, 0.0, 0.0);
ts.addNode(1.0, 0.0, 1.0);
ts.addNode(2.0, 0.0, 0.0);
ts.addElement(0, 1, 0);
ts.addElement(1, 2, 0);
ts.addElement(0, 2, 1);
System.out.println("The volume is: " + ts.getVolume());
ts.draw();
viewer.setVisible(true);
Step 5: Create Your Own Structure
Make a sketch of a interesting structure that comes into your mind. Establish a model of that structure
using the classes you have programmed.
5.3. TRUSS STRUCTURES
63
5.3.3 Reading a Structure from a File
Often it is more convenient to store the description of a structure in a text file than writing a program.
In order to do so, we first have to decide about the file format. Here, a format that is similar to that of
the Finite Element software Ansys is used. Example:
n, 0.0, 0.0, 0.0
n, 1.0, 0.0, 1.0
rcs, 0.05, 0.1
e, 0, 1, 0
The syntax is as follows: every line of the input file describes one item of the structure (i.e. a node,
an element, a cross section). Each line consists of several entries separated by commata. The first
entry is the key: n stands for node, rcs for rectangular cross section and e for element (of course,
the syntax has to be extended for other cross section types). The other entries describe the item itself.
The following code is a starting point for your implementation:
1
import java.io.*;
2
3
public class TrussStructureReader {
4
public static TrussStructure read(String filename) throws Exception {
TrussStructure ts = new TrussStructure();
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line = reader.readLine();
5
6
7
8
9
while (line != null) {
String[] items;
10
11
12
// remove trailing and leading spaces
line = line.trim();
13
14
15
// split line at commata
items = line.split("\\s*,\\s*");
16
17
18
if (items.length == 0) {
19
20
}
else if (items[0].equals("n")) {
21
22
23
}
// other cases go here
24
25
26
line = reader.readLine();
27
}
28
29
return ts;
30
}
31
32
}
Complete the code above! Useful methods: Integer.parseInt and Double.parseDouble
to convert strings into numbers.
CHAPTER 5. CONNECTING OBJECTS
64
5.3.4 Example for an Object Diagram
An object-oriented program running on a computer consists of a number of collaborating objects
residing in the computer’s memory. In order to visualize the state of a program at a certain point
in time, we can employ object diagrams depicting existing objects and connections between objects.
Object diagrams are specified in the UML (Unified Modeling Language) and have certain similarities
with class diagrams. In this course, we use a notation which differs slightly from the UML because
we depict object variables and object attributes as pointers to objects. In the book “Core Java, Volume
1 - Fundamentals” by Horstmann and Cornell you can find a similar notation.
Face
Edge
Vertex
Figure 5.4: A mesh consisting of faces
Object diagrams – as we use them – are explained at hand of an example: Consider a mesh consisting of several faces (see Fig. 5.4 and http://en.wikipedia.org/wiki/Polytope for further information).
A face is bounded by a certain number of edges where an edge has a startpoint and an endpoint. In
the UML class diagram in Fig. 5.5 we introduce four corresponding classes.
A Mesh consists of an arbitrary number of Face objects and has a method addFace which adds
an additional Face to the mesh. A Face object is associated with an arbitrary number of Edge
objects but at least three edges are required. Accordingly, the constructor of the Face class takes
an array of Edge objects. An Edge is associated with exactly two Vertex objects such that the
constructor of Edge expects two variables of type Vertex. The Vertex class has two double
attributes to store the location and a constructor taking two double parameters.
Mesh
0..*
+addFace(f: Face): void
Face
+Face(edges: Edge[])
3..*
Edge
+Edge(v1: Vertex, v2: Vertex)
2
Vertex
-x: double
-y: double
+Vertex(x: double, y: double)
Figure 5.5: UML class diagram for the mesh classes
5.3. TRUSS STRUCTURES
65
A possible implementation of the classes in Java is given below. Note that we use an ArrayList
to store the faces of the Mesh but an array of Edge objects in the Face class. The reason for this
decision is that faces can be added after a mesh has been created while the number of edges does not
change after a face has been constructed.
1
public class Vertex {
2
private double x_;
private double y_;
3
4
5
public Vertex(double x, double y) {
x_ = x;
y_ = y;
}
6
7
8
9
10
1
}
public class Edge {
2
private Vertex v1_;
private Vertex v2_;
3
4
5
public Edge(Vertex v1, Vertex v2) {
v1_ = v1;
v2_ = v2;
}
6
7
8
9
10
1
}
public class Face {
2
private Edge[] edges_;
3
4
public Face(Edge[] edges) {
edges_ = edges;
}
5
6
7
8
}
1
import java.util.ArrayList;
2
3
public class Mesh {
4
private ArrayList<Face> faces_ = new ArrayList<Face>();
5
6
public void addFace(Face f) {
faces_.add(f);
}
7
8
9
10
}
CHAPTER 5. CONNECTING OBJECTS
66
Consider the main-program given in the listing below which is supposed to create the mesh shown in
Fig. 5.4. Note that lines 16 and 17 show the shorthand form of array declaration and initialization.
1
public class MeshTest {
2
public static void main(String[] args) {
Mesh m = new Mesh();
Vertex v1 = new Vertex(0, 0);
Vertex v2 = new Vertex(1, 0);
Vertex v3 = new Vertex(2, 0);
Vertex v4 = new Vertex(0, 1);
Vertex v5 = new Vertex(1, 1);
Edge e1 = new Edge(v1, v2);
Edge e2 = new Edge(v2, v3);
Edge e3 = new Edge(v1, v4);
Edge e4 = new Edge(v2, v5);
Edge e5 = new Edge(v4, v5);
Edge e6 = new Edge(v5, v3);
Edge[] es1 = { e1, e4, e5, e3 };
Edge[] es2 = { e2, e6, e4 };
Face f1 = new Face(es1);
Face f2 = new Face(es2);
m.addFace(f1);
m.addFace(f2);
}
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
}
Mesh
faces
Edge
v1
v2
Face
edges
Edge
v1
v2
ArrayList
Face
edges
Edge
v1
v2
Edge
v1
v2
Edge
v1
v2
Edge
v1
v2
m
es1 es2
es1 es2
e1 e2 e3 e4 e5 e6
Figure 5.6: Mesh objects in an object diagram
Vertex
x=0
y=0
Vertex
x=1
y=0
Vertex
x=2
y=0
Vertex
x=0
y=1
Vertex
x=1
y=1
v5 v4 v3 v2 v1
5.3. TRUSS STRUCTURES
67
The state of the program at the end of the main method is shown in the object diagram in Fig. 5.6. In
the diagram, we have two regions separated by a dashed line.
• The lower region contains the variables we use in the part of the program we are investigating.
Variables point to objects which are shown in the upper area.
• The upper region contains the objects we have created in our program. An object is depicted as
a box with two compartments. The upper compartment contains the type name underlined while
the lower compartment contains attributes. Values of primitive type attributes are given directly
(as with the Vertex objects) while object type attributes simply point to another object.
Arrays are depicted as strips of boxes whereas the boxes of object type arrays hold pointers to
other objects.
Let us stop the main method at certain points of execution and discuss what happened.
Line 4 We have created one Mesh object and the variable m points to this object. In line 5 of the
file Mesh.java, the attribute faces of type ArrayList is declared and the corresponding
object is created. Therefore, in the diagram, we have an additional ArrayList object and the
faces attribute of our Mesh object points to this ArrayList object.
Line 9 Several Vertex objects have been created. The attributes contain the coordinates specified
during object construction.
Line 15 The required Edge objects have been created. In the constructor, the vertices of the edges
are specified and therefore the v1 and v2 attributes of the Edge objects point to the corresponding Vertex objects.
Line 17 We have created two arrays of Edge objects using a shorthand notation where the array
content is specified in just one line. The array elements hold pointers to the corresponding
Edge objects. Alternatively to the shorthand notation, we could have written
1
2
3
4
Edge[] es2 = new Edge[3];
es[0] = e2;
es[1] = e6;
es[2] = e4;
for the second edges array, for instance
Line 19 Two Face objects have been created and the arrays of edges have been passed to the constructor. Therefore, the edges attributes of the Face objects point to the corresponding array
objects.
Line 21 The two faces have been added to the mesh and the ArrayList object holds pointers to the
two Face objects. Since we are not interested in the way, the ArrayList stores the pointers
(encapsulation), we just draw pointers from the ArrayList box to the Face objects.
68
CHAPTER 5. CONNECTING OBJECTS
Chapter 6
Programming Graphical User Interfaces
6.1 Introduction
Nowadays, a graphical user interface (GUI) is the standard paradigm for realizing the interaction
between the user and an application. Other than for instance C++, Java includes a rich set of classes
for the realization of Gui’s. The standard Java class library contains mainly two packages for Gui’s
that can be used alternatively:
1. The Abstract Windowing Toolkit (AWT) is in the java.awt package. AWT was introduced
in the first version of Java and contains only the most essential classes.
2. The classes of the so-called Swing toolkit are in the package javax.swing. Swing was
introduced later in Java 1.2 and provides a richer set of classes. Using Swing, it is possible to
build professional and nice looking Gui’s. Note that Swing uses many classes from AWT.
In this course, we will use Swing. Swing contains about 140 classes and interfaces. In this chapter,
you will learn about the most basic concepts used in Swing. For further information, the Swing tutorial (http://java.sun.com/docs/books/tutorial/uiswing) provides an excellent
introduction, it covers a wide range of problems and contains many example programs. Moreover,
programming Swing without access to the javadoc documentation is impossible!
69
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
70
6.2 Swing Components
Graphical applications consist of components like windows, buttons, text fields, scrollbars, checkboxes etc. All these components are in Java represented by classes. Figure 6.1 shows some important
Swing classes. Note that the diagram also contains AWT classes (those that do not start with a J)
because Swing sits on top of AWT. It is also obvious, that the design of AWT/Swing makes strong use
of inheritance.
Graphics
Component
1
+setVisible(v: boolean): void
+getGraphics(): Graphics
+addMouseListener(l: MouseListener): void
+addMouseMotionListener(l: MouseMotionListener): void
...
+setColor(c: Color): void
+drawLine(...): void
...
0..*
Container
Canvas
...
+setLayout(l: LayoutManager): void
+add(c: Component): Component
...
JPanel
...
Window
JTabbedPane
JComponent
+addTab(t:String, c:Component): void
...
...
...
Frame
...
JFrame
...
AbstractButton
JLabel
+addActionListener(l: ActionListener): void
...
+JLabel(l: String)
...
JButton
+JButton(t: String)
...
JComboBox
+JComboBox(enries: Object[])
+getSelectedItem(): Object
...
JList
+JList(enries: Object[])
+getSelectedValue(): Object
...
JToggleButton
+JToggleButton(t: String)
...
JCheckBox
+JCheckBox(t: String)
+isSelected(): boolean
...
Figure 6.1: Essential Swing classes
Component The base class for everything that can be drawn on the screen. A Component is associated with a Graphics object on which the Component paints its graphical representation.
Also, a Component captures mouse events (see event handling later in this chapter).
JFrame A window on the screen with border and title.
6.2. SWING COMPONENTS
71
JPanel A container that you can use to layout components inside a frame.
JTabbedPane A Component that holds other components in tabs.
JLabel A short piece of text that is drawn on the screen.
JButton A button with a label that can be clicked.
JCheckBox A component that is either selected or not.
JComboBox A component that contains a set of selectable items (usually strings).
6.2.1 Using Swing Components
In order to get an idea of using Swing components, enter the code below and check out what happens
if you run it.
import java.awt.*;
import javax.swing.*;
public class SwingComponents extends JFrame {
private
private
private
private
private
JList list_;
JComboBox comboBox_;
JButton button_ = new JButton("JButton");
JCheckBox checkBox_ = new JCheckBox("JCheckbox");
JTextField textField_ = new JTextField("JTextfield");
public static void main(String[] args) {
new SwingComponents().setVisible(true);
}
public SwingComponents() {
String[] listEntries = { "Concrete", "Cast Iron", "Masonry" };
// create more complex components
list_ = new JList(listEntries);
comboBox_ = new JComboBox(listEntries);
// layout components
setLayout(new FlowLayout());
add(textField_);
add(list_);
add(button_);
add(comboBox_);
add(checkBox_);
// configure
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 300);
}
}
Listing 6.1: Using Swing components
72
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
The class is typical for the main window of a swing application:
1. It inherits from JFrame and thus represents a window on the desktop.
2. Attributes represent the visual components of the application. If possible, components are immediately constructed.
3. In the constructor of the class,
• components that need additional information are constructed (list and combo-box).
• components are added to the frame. The add method is defined in the Container class.
• we specify that the application should exit when the window closes.
• using setSize, a certain size for the window is set.
4. In the main method, we solely create the frame and make it visible.
6.3 Laying out Components
Have a look at the Container class in Figure 6.1. A Container object is a special Component
that contains other components (indicated by the association in the class diagram). In order to arrange
the components in a container, objects that implement the LayoutManager interface are used.
Figure 6.2 shows three commonly used layout managers (there are more).
<<interface>>
LayoutManager
GridLayout
+GridLayout(rows: int, cols: int)
...
BorderLayout
+NORTH: String
+WEST: String
+CENTER: String
+EAST: String
+SOUTH:String
+BorderLayout()
...
CENTER
SOUTH
Figure 6.2: Three Layout Managers
EAST
NORTH
WEST
FlowLayout
+LEFT: int
+CENTER: int
+RIGHT: int
+FlowLayout(alignment: int)
...
6.3. LAYING OUT COMPONENTS
73
N OTE : In Figure 6.2, the LayoutManager box is labeled with the stereotype interface. You can
think of an interface as an abstract class that contains only abstract methods and no (instance) attributes. Classes that provide the methods specified in an interface are said to implement that interface.
The UML notation of implementation is similar to inheritance, but a dashed line is used.
Flow Layout
A flow layout arranges components in a left-to-right flow, much like letters in a word while components retain their natural size. Using a parameter in the constructor, you can specify the orientation of
components. The example 6.1 already uses a flow layout. Check out another orientation by specifying
1
setLayout(new FlowLayout(FlowLayout.RIGHT));
in the SwingComponents constructor.
Grid Layout
A grid layout arranges the components in an equally-sized grid, components are resized as needed.
The number of rows and columns of the grid is specified in the constructor of the GridLayout class.
Use
1
setLayout(new GridLayout(3, 2));
to see how a grid layout manager works.
Border Layout
A border layout manager arranges components in five compartments: north, west, center, east and
south. The way how the component’s size is handled is special: Components in the north and south
retain natural height but are scaled in horizontal direction. The components in the west and east retain
natural width while being scaled in the height. Finally, the component in the center fills the remaining
space. Often, not all compartments are occupied. When using a border layout, the position of the
component is specified using constants defined in the BorderLayout class as second argument to
the add method. Check out
1
2
3
4
5
6
setLayout(new BorderLayout());
add(textField_, BorderLayout.NORTH);
add(list_, BorderLayout.WEST);
add(button_, BorderLayout.CENTER);
add(comboBox_, BorderLayout.EAST);
add(checkBox_, BorderLayout.SOUTH);
to understand the border layout.
6.3.1 Combining Layout Managers
In most situations, user interfaces are more complex than the above examples. In such situations
you can use the GridBagLayout layout manager which is more powerful but at the same time
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
74
notoriously complicated to use. The alternative is to use combinations of the above layout managers.
Here, several JPanel objects each having a suitable layout manager are combined.
Listing 6.2 demonstrates this approach at hand of a graphical user interface for calculating the
greatest common divisor of two integers. Enter the code to see how it works.
import java.awt.*;
import javax.swing.*;
public class GraphicalGCD extends JFrame {
public static void main(String[] args) {
new GraphicalGCD().setVisible(true);
}
private
private
private
private
private
JTextField nominatorTF_ = new JTextField(9);
JTextField denominator_ = new JTextField(9);
JLabel resultLabel_ = new JLabel();
JButton computeButton_ = new JButton("Compute");
JButton exitButton_ = new JButton("Exit");
public GraphicalGCD() {
setLayout(new BorderLayout());
// panel for textfields
JPanel p1 = new JPanel(new GridLayout(3, 2));
p1.add(new JLabel("Nominator:", JLabel.RIGHT));
p1.add(nominatorTF_);
p1.add(new JLabel("Denominator:", JLabel.RIGHT));
p1.add(denominator_);
p1.add(new JLabel("GCD:", JLabel.RIGHT));
p1.add(resultLabel_);
add(p1, BorderLayout.CENTER);
// buttons
JPanel p4 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p4.add(computeButton_);
p4.add(exitButton_);
add(p4, BorderLayout.SOUTH);
// configure
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
}
}
Listing 6.2: Combining layout managers
6.4. EVENT HANDLING
75
Here’s some explanation: In the constructor of the GraphicalGCD class, JPanel p1 is used
to hold text fields along with some description. This panel uses a grid layout which arranges the
components row by row in the sequence they have been added. JPanel p2 arranges the two buttons
aligned to the right hand side.
6.4 Event Handling
In order to add some functionality to the graphical GCD program, you have to handle events invoked
by the user of the program. The classes and interfaces involved in the event handling for a button are
shown in Figure 6.3.
JButton
0..*
+addActionListener(l: ActionListener): void
...
<<interface>>
ActionListener
+actionPerformed(e: ActionEvent): void
ActionEvent
+getSource(): Object
...
Figure 6.3: Button and Event Listener
A button is associated with a list of objects the implement the ActionListener interface. Listeners are added to the button using the addActionListener method. When the button is clicked
by the user, it invokes the actionPerformed method for each listener that has been previously
added. An ActionEvent object is passed to the listener. Using the getSource method of the
event object, the listener can determine which object initiated the event.
Implementing the ActionListener Interface
For our graphical GCD example, we first have to implement the ActionListener interface. This
is conveniently done in the current class i.e. GraphicalGCD. By changing the class header to
1
public class GraphicalGCD extends JFrame implements ActionListener {
we specify, that the GraphicalGCD class implements the ActionListener interface. Next, the
actionPerformed method specified in the ActionListener interface must be implemented.
Let us start with a simple “dummy” implementation that just prints out a string:
1
2
3
public void actionPerformed(ActionEvent e) {
System.out.println("Action performed");
}
Registering the Listeners
Next, we have to tell the two buttons, that the program wants to be notified when buttons are clicked.
In order to do so, add the following two lines to the constructor of the GraphicalGCD class:
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
76
1
2
computeButton_.addActionListener(this);
exitButton_.addActionListener(this);
If you now run your program, the string “Action performed” should appear on the console when you
click one of the two buttons.
Getting the Event Source
For the correct behavior of the program, we finally have to distinguish whether the “Compute” or the
“Exit” button has been clicked. For that, the ActionEvent class provides a method getSource
that returns the object that caused the event. Add the following code to the actionPerformed
method. Note that the exit method of the System class is used to terminate the application.
1
2
3
4
public void actionPerformed(ActionEvent e) {
if (e.getSource() == computeButton_) {
String result = nominatorTF_.getText() + " and "
+ denominatorTF_.getText();
5
resultLabel_.setText(result);
} else if (e.getSource() == exitButton_) {
System.exit(0);
}
6
7
8
9
10
}
Assignment: Implement the computation of the greatest common divisor correctly. Use Integer.parseInt
to convert the content of the text fields into a number.
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
77
6.5 The Model-View-Controller Pattern
For the implementation of graphical user interfaces, the so called model-view-controller (MVC) pattern has proven to be a practical way to organize responsibilities within the application. In this section,
we develop an interactive program to create and manipulate truss-structures (previous chapter) using
the MVC approach. The desired functionality of the program is as follows:
• The structure is modified using textual commands issued in a command line (similar to e.g.
AutoCAD or Ansys).
• A graphical area shows a 3D representation of the structure.
• Additionally, nodes, sections and elements are textually represented in a table.
Figure 6.4 shows a possible layout of the program.
listing
of
nodes...
graphical display
command line
Figure 6.4: Sketch of an interactive truss-structure program
According to the MVC pattern, the application logic is split into Model, View and Controller:
Model The domain specific information on which the application operates. In our case, the model is
the truss structure.
View Renders the model to the user, either in a graphical or a textual representation. In our application, we have a graphical view of the model and several textual views.
Controller The controller takes input from the user and modifies the model accordingly. Obviously,
the command line is the controller in our case.
The most important aspect of MVC is that the model keeps a list of associated views and provides a
method that notifies all views about a change of the model. This makes it easy to add views without
having to change much code.
Basically, the control flow in a MVC application is as follows:
1. The user interacts with the application (enters a command, clicks a button, . . . ).
2. A controller receives the user input and modifies the model (eg. adds a node or an element).
Then, the controller asks the model to inform the views that something has changed.
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
78
3. The views retrieve the current state of the model and update themselves accordingly.
Figure 6.5 shows the MVC pattern in its simplest form.
interacts
Controller
user
modifies
notifies
View
Model
retrieves state
Figure 6.5: Model-view-controller, simple version
6.5.1 Step 1: Preliminaries
Before we can start with the graphical application, the truss structure classes from the previous chapter
have to be extended (you can download everything discussed here from the course web-site).
First of all, we need an interface, that specifies a view on a truss structure:
public interface TrussStructureView {
public void update(TrussStructure ts);
}
Listing 6.3: Truss structure view interface
Next, we have to add functionality to the truss-structure class. The TrussStructure class needs
the list of views, a method to add a view and another method to notify all views.
1
private ArrayList views_ = new ArrayList();
2
3
4
5
public void addView(TrussStructureView view) {
views_.add(view);
}
6
7
8
9
public void notifyViews() {
for (int i = 0; i < views_.size(); i++) {
TrussStructureView view = (TrussStructureView) views_.get(i);
10
view.update(this);
11
}
12
13
}
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
79
In order to generate the various views of the truss structure, we need several methods to access the elements of the truss structure. Therefore, the following methods have to be added to the TrussStructure
class:
1
2
3
4
5
public void clear() {
crossSections_.clear();
nodes_.clear();
elements_.clear();
}
6
7
8
9
public int countCrossSections() {
return crossSections_.size();
}
10
11
12
13
public CrossSection getCrossSection(int idx) {
return (CrossSection) crossSections_.get(idx);
}
14
15
16
17
public int countNodes() {
return nodes_.size();
}
18
19
20
21
public Node getNode(int idx) {
return (Node) nodes_.get(idx);
}
22
23
24
25
public int countElements() {
return elements_.size();
}
26
27
28
29
public Element getElement(int idx) {
return (Element) elements_.get(idx);
}
30
31
32
33
public int indexOf(Node node) {
return nodes_.indexOf(node);
}
34
35
36
37
public int indexOf(CrossSection cs) {
return crossSections_.indexOf(cs);
}
Also, the Element class has to be extended:
1
2
3
4
5
6
7
8
9
public CrossSection getCrossSection() {
return crossSection_;
}
public Node getNode1() {
return node1_;
}
public Node getNode2() {
return node2_;
}
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
80
Finally, we need a class that interprets commands issued on the command line. The idea is, to use a
simple syntax of single commands where the arguments are separated by commas. For instance, the
sequence
1
2
3
4
5
n,0,0,0
n,5,0,0
ccs,0.1
e,0,1,0
save,struct.txt
should create two nodes, a circular section and an element. Finally the structure is saved to a file
”struct.txt”. Note that the syntax is similar to the one used by the commercial FE-Code Ansys.
For the purpose of interpreting user commands, the Interpreter class is introduced. Here’s a
fragment of the class:
1
public class Interpreter {
2
public static void executeCommand(String command, TrussStructure ts) {
String[] args = command.split(",");
String cmd = args[0].trim();
3
4
5
6
try {
// a node
if (cmd.equals("n")) {
double x = Double.parseDouble(args[1]);
double y = Double.parseDouble(args[2]);
double z = Double.parseDouble(args[3]);
7
8
9
10
11
12
13
ts.addNode(x, y, z);
}
// other commands omitted
} catch (Exception e) {
System.out.println("Illegal command: " + command);
}
14
15
16
17
18
19
}
20
21
}
Download the complete class from our course pages.
The remaining part of this chapter is handed out next exercise!
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
81
Commands understood by the interpreter
Currently, the interpreter understands the following commands:
n,x,y,z
Create a node at the specified location
rcs,w,h
Create a rectangular section of the specified width and height
ccs,r
Create a circular section of the specified radius
e,idxN1,idxN2,idxCs
Create an element using the specified node and the specified section
save,filename
Saves the current structure to the specified file
open,filename
Reads the commands in the specified file
In the case that you want to use your own cross-section classes (e.g. I-shaped), extend the interpreter class accordingly.
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
82
6.5.2 Software Design
The software design for the truss-structure editor is shown in Figure 6.6. The TrussStructureEditor
class represents the application frame on the screen and is at the same time the entry point to the program (main-method). According to the interface design in Figure 6.4, a truss structure editor contains
a CommandPanel, a GraphicalView and a SidePanel.
The CommandPanel is the controller that modifies the TrussStructure object representing
the model in the MVC application. After each user interaction, the controller tells the model to notify
all dependent views to update using the notifyViews method.
TrussStructure
JFrame
+addView(v: TrussStructureView): void
+notifyViews(): void
...
0..*
<<interface>>
TrussStructureView
+update(ts: TrussStructure): void
JPanel
1
TrussStructureEditor
+main(args: String[]): void
+TrussStructureEditor()
CommandPanel
1
+CommandPanel(ts: TrussStructure)
VTKCanvas
GraphicalView
1
+GraphicalView(ts: TrussStructure)
+update(ts: TrussStructure): void
ListSectionsPanel
+ListSectionPanel(ts: TrussStructure)
1 +update(ts: TrussStructure): void
1
ListNodesPanel
SidePanel
+SidePanel(ts: TrussStructure)
1 +ListNodesPanel(ts: TrussStructure)
+update(ts: TrussStructure): void
ListElementsPanel
1
+ListElementsPanel(ts: TrussStructure)
+update(ts: TrussStructure): void
Figure 6.6: UML class diagram of the truss structure editor application
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
83
In the GraphicalView, the truss-structure is rendered. Because the class represents a view in
the MVC application, it implements the TrussStructureView interface. Moreover, it inherits
from VTKPanel – an existing class in the VTK package – and is therefore capable of generating a
3D view of the model.
Finally, the SidePanel is shown at the left-hand side of the application frame and contains listings of cross-sections, nodes and elements. These listings are contained in the ListSectionsPanel,
ListNodesPanel and ListElementsPanel objects. Again, these classes implement the
TrussStructureView interface.
6.5.3 Implementation
The following pages will guide you step by step through the implementation of the truss-structure
editor application.
The TrussStructureEditor class
Let us start with the main class of the application; it will be extended later as needed. Create the
class according to the class diagram. In the main-method, create the frame and set it visible. For
the moment, the constructor just creates a truss-structure object, specifies a layout manager and does
some configuration:
1
2
public TrussStructureEditor() {
TrussStructure ts = new TrussStructure();
3
setLayout(new BorderLayout());
4
5
setSize(800, 600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
6
7
8
}
Run your application for the first time; you should see an empty frame.
The CommandLinePanel class
The CommandLinePanel class basically contains a JTextField that captures the user input
and registers itself as ActionListener at the JTextField which raises an ActionEvent
when the user hits the return-key. Now implement the CommandLinePanel class; it inherits from
JPanel and implements the ActionListener interface. The text field is stored in an attribute.
In the constructor,
• the panel registers itself as action listener at the text field.
• the textfield is added to the panel. Use a GridLayout with 1 × 1 cells such that the textfield
fills the entire panel.
• Finally, don’t forget the association to the truss structure object.
In the actionPerformed method, the content of the text field is used as a command to the
Interpreter.executeCommand method. Then, the text field is cleared and the observers of
the truss structure are notified.
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
84
Here’s the corresponding code (adapt the attribute names to your choice):
1
2
public void actionPerformed(ActionEvent arg0) {
Interpreter.executeCommand(textField_.getText(), structure_);
3
textField_.setText("");
structure_.notifyViews();
4
5
6
}
After you completed the class, include it in the constructor of the TrussStructureEditor class
and add it to the frame in the south-compartment of the border layout. Run the application and issue
some commands in the command line.
The GraphicalView class
Step 1 The graphical view shows the nodes and the elements of the truss structure. According to
the UML class-diagram in Figure 6.6, the base class for GraphicalView is VTKPanel which is
capable of rendering 3D objects; also it implements the TrussStructureView interface. Here’s
a starting point for the implementation of the class:
1
2
3
import java.util.*;
import inf.vtk.*;
import inf.v3d.obj.*;
4
5
public class GraphicalView extends VTKCanvas implements TrussStructureView {
6
public GraphicalView(TrussStructure ts) {
ts.addView(this);
}
7
8
9
10
public void update(TrussStructure ts) {
// remove all objects currently visible
GetRenderer().RemoveAllViewProps();
11
12
13
14
// create 3D objects
updateNodes(ts);
updateElements(ts);
15
16
17
18
// update view
resetCamera();
Render();
19
20
21
}
22
23
private void updateNodes(TrussStructure ts) {
System.out.println("Updating nodes");
}
24
25
26
27
private void updateElements(TrussStructure ts) {
System.out.println("Updating elements");
}
28
29
30
31
}
In the constructor, the graphical view just adds itself as view to the model. In the update method,
first all element that are currently visible are removed. Then, nodes and elements are updated and
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
85
finally, the view is adjusted and the elements are rendered.
Enter the program code listed above and add a GraphicalView object to the center of your
TrussStructureEditor frame. Run your program; when you enter commands in the command
line, you should see the output of the print statements above.
Step 2 The next step is to implement the updateNodes method. In order to represent the nodes
visually, we use Sphere objects that are located at the nodes’ positions. The following code does the
job:
1
2
3
4
private void
for (int i
Node n =
Sphere s
updateNodes(TrussStructure ts) {
= 0; i < ts.countNodes(); i++) {
ts.getNode(i);
= new Sphere(n.getX(), n.getY(), n.getZ());
5
s.setRadius(0.1);
s.setColor("BLUE");
GetRenderer().AddActor(s.getActor());
6
7
8
}
9
10
}
Note the last line: Here, we have to use VTK methods in order to make the sphere visible on the
screen.
Step 3 Finally, program the updateElements method yourself. Don’t forget to add the actor of
the extrusion object to the renderer.
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
86
The ListNodesPanel class
The purpose of the ListNodesPanel class is to provide a textual representation of nodes. Such
information can be conveniently presented using a JTable object (JTable is a powerful class that
is part of Swing). Here is the code of the ListNodesPanel class:
1
2
3
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
4
5
public class ListNodesPanel extends JPanel implements TrussStructureView {
6
private JTable table_ = new JTable();
7
8
public ListNodesPanel(TrussStructure structure) {
structure.addView(this);
9
10
11
// add components
setLayout(new GridLayout(1, 1));
add(new JScrollPane(table_));
setBorder(BorderFactory.createTitledBorder("Nodes"));
12
13
14
15
}
16
17
public void update(TrussStructure structure) {
int nn = structure.countNodes();
DefaultTableModel tm = new DefaultTableModel(nn, 4);
String[] cis = { "Id.", "X", "Y", "Z" };
18
19
20
21
22
tm.setColumnIdentifiers(cis);
for (int i = 0; i < nn; i++) {
Node n = structure.getNode(i);
23
24
25
26
tm.setValueAt("" + i, i, 0);
tm.setValueAt(n.getX(), i, 1);
tm.setValueAt(n.getY(), i, 2);
tm.setValueAt(n.getZ(), i, 3);
27
28
29
30
}
table_.setModel(tm);
31
32
}
33
34
}
The JTable object associated with our ListNodesPanel is as usual stored in an attribute. In the
constructor, first the ListNodesPanel adds itself as a view to the structure. Then, a GridLayout
object is specified as layout manager. Because 1 × 1 cells are used for the grid layout, the JTable
added in the next line fills the complete panel. When adding the table, it is embedded in a JScrollPane objects that adds scrollbars to the table if needed. Finally, a titled border is specified for the
panel.
The update method populates the table with the node coordinates. Interestingly, the JTable
class follows the MVC pattern: The data is stored in an object that implements the TableModel
interface. The corresponding JTable object renders the data on the screen. Moreover it can also
be used to modify the data. Therefore, a JTable object is at the same time view and controller.
In the update method, an object of type DefaultTableModel is used to store the table data.
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
87
The DefaultTableModel class is a straightforward default implementation of the TableModel
interface. When the DefaultTableModel is constructed, the number of rows and columns are
specified. Then, headers for the colums are specified. In the loop over all nodes, the individual cells
of the table model are populated. Finally, the table model object is passed to the table.
The SidePanel class
The SidePanel hosts the three list panels and is straightforward. A grid layout with three rows
and one column is used. The ListNodesPanel is constructed and added to the panel in one step.
Finally, a certain size is specified such that the side panel is wide enough to display its content later.
1
2
import java.awt.*;
import javax.swing.*;
3
4
public class SidePanel extends JPanel {
5
public SidePanel(TrussStructure ts) {
// layout components
setLayout(new GridLayout(3, 1));
add(new ListNodesPanel(ts));
6
7
8
9
10
// configure
setPreferredSize(new Dimension(200, 100));
11
12
}
13
14
}
Now go to the constructor of the TrussStructure class. Create a SidePanel object and add it
to the west-compartment. Run your application and see the nodes being listed as you create them.
The ListSections and ListElements classes
These classes are very much similar to the ListNodes class. Implement them on your own. Hints:
Use getClass().getName() to find out the name of a cross section object. When listing elements, the indexOf methods may be useful to find out the index of nodes and cross-sections.
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
88
6.5.4 Creating Nodes and Elements Using the Mouse
The last and most difficult task is to create nodes and element by clicking in the graphical viewer.
For that, we have to implement a controller that captures user input from a view. By that, the get the
(mostly) complete form of the Model-View-Controller pattern shown in Figure 6.7. The difference
to Figure 6.5 is that a controller now also listens to events generated by a view and that it performs
model modifications according to these events.
interacts
Controller
user
interacts
modifies
generates event
notifies
View
Model
retrieves state
Figure 6.7: Complete model-view-controller pattern
Preliminaries
In order to be able to create elements by clicking nodes, the TrussStructure class needs a
method that finds a node that is close to a certain position in space. Here it is; add it to your
TrussStructure class.
1
2
3
4
public Node findNode(double x, double y, double z) {
for (int i = 0; i < countNodes(); i++) {
Node n = getNode(i);
double d = n.distance(new Node(x, y, z));
5
if (d < 0.1) {
return n;
}
6
7
8
}
return null;
9
10
11
}
Note that a predefined radius of 0.1 is used as tolerance. For a more sopisticated application, this
value had to be adjusted according to the size of the model.
Implementing the Controller
The controller to create nodes and elements is realized in the EditorToolbar class which inherits
from JToolbar such that it can be part of the application’s toolbar.
The purpose of this controller is to capture mouse events from the graphical view and to translate
these mouse events into commands that create nodes or elements. Therefore, it must implement
the MouseListener interface and register itself as listener at the graphical view. Moreover, the
controller needs to know the graphical view and the truss structure. As usual, we use attributes to
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
89
store these associations. Let us start with a basic implementation and add things later step by step.
Here’s the starting point:
public class EditorToolbar extends JToolbar implements MouseListener {
1
2
private GraphicalView graphicalView_;
private TrussStructure structure_;
3
4
5
public EditorToolbar(TrussStructure ts, GraphicalView gv) {
// initialize attributes
graphicalView_ = gv;
structure_ = ts;
6
7
8
9
10
// add components
add(new JLabel("Remove me later"));
11
12
13
// add listeners
graphicalView_.addMouseListener(this);
14
15
}
16
17
}
Enter the code above and let Eclipse help you add the methods defined in the MouseListener
interface: Put the cursor into the name of the class (underlined in red) and press ctrl+1. Eclipse
suggests to add the unimplemented methods. Modify the mouseClicked method such that you can
see that something happens:
1
2
3
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked");
}
Now, let us add the toolbar to the application. Go to your TrussStructureEditor class and add
an EditorToolbar to the north of the TrussStructureEditor frame. Run your application,
click inside the graphical view and see the print statements.
Getting the Pick Position in 3D Space One difficulty in interactive 3D graphics is to convert 3D
screen coordinates into 3D world coordinates. Fortunately, VTK provides the vtkWorldPointPicker
class that helps us. In the getPickPosition method, first we set up the screen coordinates as expected by VTK. Then, we use the picker object to get the 3D coordinates corresponding to our mouse
event. Add the code to your EditorToolbar class.
1
2
3
4
5
6
7
8
9
10
11
12
private double[] getPickPosition(MouseEvent e) {
double[] worldCoords;
vtkWorldPointPicker picker = new vtkWorldPointPicker();
// screen coordinates
int screenX = e.getX();
int screenY = graphicalView_.getSize().height - e.getY();
double[] screenCoords = { screenX, screenY, 0 };
// convert into world coordinates and return
picker.Pick(screenCoords, graphicalView_.GetRenderer());
worldCoords = picker.GetPickPosition();
return worldCoords;
}
90
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES
Creating Nodes Having the getPickPosition method, creating nodes is easy. Add a method
createNode(MouseEvent e) and invoke it from within the mouseClicked method. In order
to create the node, first convert the mouse event into 3D coordinates, then create the node and finally,
notify all observers of the truss structure. Implement the methods and check out if your code works.
Creating Elements Currently, we are only able to create nodes; whenever you click, a new node is
added to the structure. This is not what we want!
In order to create either nodes or elements, the user must be able to tell the program what he/she
wants to do. Moreover, when creating elements, we have to know the index of the cross-section to
use. Therefore, two JToggleButton objects and one JTextField objects are introduced. Add
the corresponding attributes to your class:
1
2
3
private JToggleButton createNodeB_ = new JToggleButton("Node");
private JToggleButton createElementB_ = new JToggleButton("Element");
private JTextField crossSectionIdTF_ = new JTextField("0", 3);
Not that the second argument in the constructor of JTextField specifies the width of the text field.
Next, the components have to be added to the toolbar in the EditorToolbar constructor:
1
2
3
4
5
6
// add components
add(createNodeB_);
add(createElementB_);
add(new JSeparator(JSeparator.VERTICAL));
add(new JLabel("Section Index", JLabel.RIGHT));
add(crossSectionIdTF_);
In order to separate the buttons from the textfield and its label, a JSeparator is used. Run you
program and see how it looks like. If you don’t want the text field to take the whole remaining toolbar
space, you can specify a maximum size for this component:
1
crossSectionIdTF_.setMaximumSize(crossSectionIdTF_.getPreferredSize());
In the mouseClicked method, we now can use the selection status of the buttons in order to decide
whether nodes or elements should be created:
1
2
3
4
5
if (createNodeB_.isSelected()) {
createNode(e);
} else if (createElementB_.isSelected()) {
createElement(e);
}
6.5. THE MODEL-VIEW-CONTROLLER PATTERN
91
The createElement method is a bit tricky because we need two nodes for an element. We do
the following: An attribute is introduced to store the first node being selected:
1
private Node node1_ = null;
If node1 is null, it means that the selected node is the first node. If it is non-null, the selected node
is the second node and we can go on creating the element. Here’s the code:
1
2
private void createElement(MouseEvent e) {
double[] p = getPickPosition(e);
3
if (node1_ == null) {
node1_ = structure_.findNode(p[0], p[1], p[2]);
} else {
Node node2 = structure_.findNode(p[0], p[1], p[2]);
4
5
6
7
8
if (node2 != null && node2 != node1_) {
int idxCs = Integer.parseInt(crossSectionIdTF_.getText());
int idxN1 = structure_.indexOf(node1_);
int idxN2 = structure_.indexOf(node2);
9
10
11
12
13
node1_ = null;
structure_.addElement(idxN1, idxN2, idxCs);
structure_.notifyViews();
14
15
16
}
17
}
18
19
}
Check out if your program works. You may realize that it is possible to activate both buttons, the
one for the nodes and the one for the elements. In order to overcome this unwanted feature, we have
to deactivate the other button if one button becomes activated. For that the EditorToolbar class
also implements the ActionListener interface and the constructor registers the object at the two
buttons:
1
2
createNodeB_.addActionListener(this);
createElementB_.addActionListener(this);
In the actionPerformed method, we just deselect buttons if needed:
1
2
3
4
5
6
7
8
public void actionPerformed(ActionEvent e) {
if (e.getSource() == createElementB_ && createElementB_.isSelected()) {
createNodeB_.setSelected(false);
}
if (e.getSource() == createNodeB_ && createNodeB_.isSelected()) {
createElementB_.setSelected(false);
}
}
92
CHAPTER 6. PROGRAMMING GRAPHICAL USER INTERFACES