CE303.1
Java OOP Basics
OOP = “Object Oriented Programming”
Java basics
Primitive data types
Arrays
Objects and classes: constructors, methods, fields
Instance features vs static features
Please see CE152/ CE203 material or online Java tutorials if you need a refresher
Inheritance
Object is an instance of a class
A class in Java/C# has a parent class:
There is always exactly one parent class (except for Object)
If the parent class is not specified, the parent class is Object
The child class inherits all the members of the parent class, even if they become ‘invisible’
A class can also implement any number of interfaces
Inheritance
A class can extend another class
the subclass inherits from the super (“parent”) class
A subclass inherits all features from its parent
Attributes and methods
Also inherits inherited features
Some inherited features may not be accessible
A subclass can hide parent attributes
Careful, this can be very confusing!
We’ll talk more about it later
A subclass can override parent methods
In Java, a class extends at most one other class
single-rooted hierarchy, no multiple inheritance
every class inherits from java.lang.Object
Hiding : Happens during inheritance (between superclass and subclass).
Shadowing : Happens within a class (between a member variable and local variable).
Difference between hiding and shadowing in java
3
The Root: java.lang.Object
All classes are descendants of Object, directly or indirectly
Constructor: Object()
Methods of class Object
protected Object clone()
public boolean equals(Object otherObject)
protected finalize() //called by garbage collector
public final Class> getClass()
public int hashCode()
public String toString()
public void final notify(), notifyAll(), wait()
Inheritance – Overriding a method
To override a super class method in a sub class
declare a method with the same name and the same number and type of parameters in the same order, and a compatible return type
In Java any method of the super class can be overridden unless it is declared final.
Also, the overriding method has to be “at least as visible” as
the original method.
you can not make a public method have package access by overriding it
The “compatible return type” means that the overriding
method can return a more specialized result
“covariant return types”
Recommendation: use @Override annotation
public class Employee {
protected final String name;
protected double salary;
public Employee(String aName, double aSalary) {
name = aName; salary = aSalary; }
public void setSalary(double aSalary) {salary = aSalary;}
public String getName() { return name; }
public double getSalary() { return salary; }
}
public class Manager extends Employee {
public double bonus;
public Manager(String aName, double aSalary, double aBonus) { super(aName, aSalary); bonus = aBonus; }
void setBonus(double aBonus) {bonus = aBonus;}
public double getSalary() { return salary+bonus; }
}
6
“Covariant Return Type” Example
public class Employee {
…
public Employee copy(){
return new Employee(name, salary);
}
}
public class Manager extends Employee {
…
public Manager copy(){
return new Manager(name, salary, bonus);
}
}
Invoking Superclass Methods & Constructors
A class can not access private features of its super class.
Superclass methods
if a subclass overrides a parent method, use super
keyword to force method call of parent
there is no super.super (it would bypass the parent class)
Superclass constructors
Can explicitly use super to call a parent class construct. Must be first statement in subclass constructor in this case.
If a constructor does not call super explicitly, then the superclass must have a constructor without parameters which will be called implicitly
Calling super-class constructors happens before initialisation and execution of rest of constructor body.
toString()
Returns a string representation
especially useful for debugging
Method is used silently in string concatenation
aString + anObject
means
aString + anObject.toString()
Object.toString() gives class name and object address.
Some IDES have a wizard for generating toString() methods
Use it!
9
public class Employee {
…
public String toString() {
return getClass().getName() +
“[name=” + name + “, salary=” + salary + “]”;
}
}
public class Manager extends Employee {
…
public String toString() {
return super.toString() +
“[bonus=” + bonus + “]”;
}
}
Overriding toString()using super
10
Employee e = new Employee(“Jane, 10000);
Employee m = new Manager(“Big Boss”, 20000, 500);
System.out.println(“e = ” + e);
System.out.println(“m = ” + m);
What is the output?
Exercise
11
Hiding Attributes and Class Methods
An attribute in a subclass hides an attribute with
the same name in a parent class
even if attribute has different type
hiding of attributes is in general not recommended
be careful not to accidentally hide attributes
the parent class attribute can still be accessed using the keyword super / base (C#)
A static method in a subclass “hides” a static method with the same name in a parent class.
Can still call the “hidden” method by prefixing the method
name with the class name.
Static
static keyword means that the member is shared by all the instances of the class, e.g. field x can be accessed from any instance of A:
class A
{
private static int x = 0;
…
}
One cannot access this or any non-static members from a static method
Do not change every member to static to avoid compilation errors; reconsider your design instead!
Abstract Classes and Methods
An abstract class cannot be instantiated – you can not create objects belonging to such a class.
Example:
java.util.AbstractCollection
An abstract class can contain
abstract (= un-implemented) methods
implemented methods
data members.
Abstract methods can only be defined in abstract classes and in interfaces
Abstract classes are useful to define “incomplete” roots of an inheritance hierarchy
Concrete subclasses must implement all abstract methods.
Shape Class
public abstract class Shape {
protected double x;
protected double y;
public abstract double area();
public abstract double perimeter();
public String toString(){ …}
}
Extending an Abstract Class
Shape
double x
double y
double area()
double perimeter()
String toString()
Circle
double r
Circle(x,y,r)
double area()
double perimeter()
Rectangle double a
double b
Rectangle(x,y,a,b) double area() double perimeter()
Why/when use Inheritance?
Different classes may have variations of the same operation that share certain parts
put shared code into the super class
this avoids duplication of code which is a good thing
Inheritance should only be used when there is an “is-a”
relationship between parent and subclass.
The subclass extends the parent with additional features
Car is a subclass of Vehicle? Okay
Car is a subclass of Driver? NO! A driver HAS a car.
Object Composition as an Alternative to Inheritance
Often, it is preferable to compose new classes from old classes by using objects as fields.
Instead of inheriting methods, the new class delegate requests to the field objects
The CalculatorWatch example below shows three design alternatives for combing two existing classes into a new class
No multiple inheritance in Java
Calculator
calculatorState
enterCalcMode()
inputNumber(n)
Watch
watchState
setTime(t)
getTime()
CalculatorWatch
watch: Watch
calc: Calculator
setTime(t) getTime() enterCalcMode() inputNumber(n)
Object Composition (Delegation)
Calculator
calculatorState
enterCalcMode()
inputNumber(n)
Watch
watchState
setTime(t)
getTime()
CalculatorWatch1
calc: Calculator
enterCalcMode() inputNumber(n)
CalculatorWatch2
watch: Watch
setTime(t) getTime()
Combining Inheritance with Composition
The Keyword final
Uses of final in Java:
On a variable of a primitive type: means value cannot be changed
Remember it’s a good practice to use constants instead of “magic numbers” all over your code!
On a variable of a reference type: means reference cannot be changed
On a method: means it cannot be overridden
On a whole class: means it cannot be extended
Why make a class final?
compiler can generate faster code, no late bindings
safety: no unwanted extensions of class
About final variables
Can you change the state of the object to which a final
variable refers?
For example is the following code legal?
final List
new ArrayList<>();
xs.add(5);
From the previous slide we said: on a variable of a reference type: means reference cannot be changed. So yes, we can change value, but not the reference!
22
Interfaces
An interface can be seen as a contract on classes
A class that implements the interface must comply with the contract.
A class can implement more than one interface
Interfaces are types, but they are not classes
they cannot be instantiated
Interfaces can contain
constants
method signatures (= implicitly abstract)
default methods (NEW)
static methods (NEW)
Using Interfaces
An interface cannot be instantiated, but you can use it as the type of variables, or method arguments and results :
List
Here is a generic sum method that works on an interface type.
public static int sum (Collection
int result = 0;
for (int x : xs)
result += x;
return result;
}
Enhanced for-loop (“for-each”)
for (T x : xs) …
is a shorthand for
Iterator
while (it.hasNext()) {T x = it.next(); … }
Shorter, more readable code
very much recommended
But not as powerful as using iterators directly,
for example, can not remove visited elements, or iterate “in sync” over two collections at the same time
Can also be used for arrays, and for any class implementing
the Iterable interface.
In Praise of Interfaces
“Programming to interfaces” features reference variables of interface type.
This helps to make code more reusable, because the code can only refer to interface methods.
Interfaces have no “single inheritance” restriction.
Interfaces are essential in larger projects for
laying down minimal requirements about a component
supporting separate compilation of components
testing different implementations of an interface
improving maintainability
Polymorphism
Polymorphism code works for more than one type
recognisable by the presence of interfaces, super-classes or generics
Examples:
Collections.sort() will sort any list provided the class of elements implements interface Comparable
Method sum() in the earlier slide works for any class implementing the interface Collection
Static Typing and Dynamic Binding
Polymorphism means that the class of an object might not be known until runtime. In these cases, the compiler does not know which method will need to be executed.
Static typing happens at compile time. Its aim is to ensure that
objects have the required operations at runtime.
At runtime the executed operation is chosen according to the class of the object.
This is known as dynamic binding.
Polymorphism (Dynamic Binding)
Polymorphism enables code to change behaviour depending on the type of the object; hence polymorphism uses dynamic binding
A method can be redefined in a child class, and whenever that method is called for an object of the child class, the redefined implementation will be invoked
In Java, polymorphism is always enabled
Non-static methods never shadow each other
The implementation of the method is selected based on the actual type of the object, not the type of the variable (hence dynamic binding)
Polymorphism: example (Java)
class A {
protected int x;
public A(int x) { this.x = x; }
public int getValue() { return x; }
}
class B extends A {
public B(int x) { super(x); }
@Override
public int getValue() { return x * 2; }
}
…
public static void main(String[] argv) {
A a = new A(5);
A b = new B(5);
System.out.println(a.getValue());
System.out.println(b.getValue());
}
Polymorphism: memory layout of objects
Reference
Type A reference
x: 5
Class A metadata
getValue() reference
a:
Reference
Type B reference
x: 5
Class B metadata
getValue() reference
b:
b.getValue() is executed as follows:
JVM follows the object reference to find the b object on the heap
JVM follows the type reference to find the information about type B
JVM follows the reference to the getValue() method implementation for type B
Observe that the type of the variable b is irrelevant in this mechanism
Overloading (Static Binding)
Java supports overloading, i.e. methods with the same name but different signatures:
double average (double x, double y) {…}
double average (double[] x) {…}
double average (Collection
Overloading is resolved using static binding, i.e. the binding happens at compile time
In other words, the actual type of an object passed to a method does not affect which method will be invoked
See example on next slide; what’s the output of this program?
Quiz: What does this Program Print?
public class OverloadingStatic { static int b, t, o = 0;
public static void count (JButton x) { b++; }
public static void count (JToggleButton x) { t++;}
public static void count (Object x) { o++; }
public static void main (String[] args) {
Object[] objects = { new JButton(), new JToggleButton(), new Object() };
for (int i = 0; i < objects.length; i++)
count(objects[i]);
System.out.println(b + " : " + t + " : " + o);
}
}
Output is 0 : 0 : 3
33
Visibility Modifiers in Java
public: access by any class
protected: may be accessed by
other classes within the same package as well as by
subclasses declared in another package
private: access only by this class
no visibility modifier = "package access"
only from the same package
NOT from subclasses declared in another package
this is more restrictive than protected
Visibility does not distinguish read and write access
Nested Classes
Inner classes are defined as members of a class
can be static or instance members
if they are instance, each inner class object is an extension of an outer class object, see image next slide
You can also define classes within some block of program statements
Local classes are declared with a name, so they can be reused in several places within some block
Anonymous classes are expressions that are used to create an object for some interface "on the fly". These cannot be re-used, as they have not been given a name.
Nested classes are useful for helper classes that are only needed within one class
There are two types of nested classes non-static and static nested classes. The non-static nested classes are also known as inner classes.
35
[Sun Tutorial]
Nested Class Use Case: Event Handlers
In GUI applications, events are generated by the user’s
interaction with e.g., the mouse and keyboard.
These are captured and processed by the OS
Then sent to Java.
E.g. pressing the mouse button might generate a mouseClicked event or a windowClosing event depending on the position of the pointer.
The event-handling code is often unique to the particular GUI application
Hence it makes sense to use nested classes instead of
defining the handlers as top-level classes.
// Local class example
public class GreetingButtonDemo {
private static final int FRAME_WIDTH = 100;
private static final int FRAME_HEIGHT = 100;
public static void main(String[] a) {
JFrame f = new JFrame();
JButton b = new JButton("Click me!");
final JLabel jl = new JLabel("Hi!");
class ButtonListener implements ActionListener {
public void actionPerformed (ActionEvent event) {
if (jl.getText().equals("Hi!")) jl.setText("Ho!");
else jl.setText("Hi");
jl.repaint();
}
}
b.addActionListener(new ButtonListener());
…
}
}
More on Local and Anonymous Classes
These classes are declared/occur within a block of program statements.
They have access to any local variables, method parameters and exception parameters in scope provided these are declared final or they are “effectively” final
This restriction means that the class can get a one-time
snapshot of its environment when it is created (“closure”)
Example: see usage of variable ”jl” in programs above.
Actually, it would compile even without final in the variable declaration because the compiler can work out that ”jl” is effectively final.
Lambda Expressions: Motivation
Anonymous class syntax is somewhat awkward
It feels particularly clumsy in case of SAM ("single abstract method", "functional") interfaces which only require the implementation of one method.
Like ActionListener, Runnable, Comparator, etc
Java 8 has introduced “lambda expressions”
Block of code with parameters
Convenient way to implement functional interfaces.
Like nested classes, lambda-expressions can access "effectively final" variables from the enclosing scope.
Lambda Expression Syntax
Parameter List
(int x, int y)
Arrow Token
->
Body
x + y
The body can be either a single expression or a block.
In the expression form, the body is simply evaluated and returned.
In the block form, the body is evaluated like a method body and a return statement returns control to the caller of the anonymous method. If the body produces a result, every control path must return something or throw an exception.
The parameter list can be empty: () -> …
Parameter types can be omitted. The compiler will in this case
infer types based on the “target type” of the place in the program
where the lambda expression occurs.
If there is only one parameter, the brace can be omitted.
Lambda Expression Examples
ActionListener buttonListener = event -> {
if (jl.getText().equals(“Hi!”))
jl.setText(“Ho!”);
else
jl.setText(“Hi!”);
jl.repaint();
};
books.forEach(b -> System.out.println(b));
Collections.sort(books, (fst,snd) -> fst.getIsbn().compareTo(snd.getIsbn()));
42
Command-Line Arguments
void main(String[] args)
args parameter of main() is initialized with the command-line argument(s)
Example:
java GreeterTest Mars
args.length is 1
args[0] is “Mars”
Quick-and-easy way to select program mode or to specify locations of input/ output files
especially useful in scripting
Annotations
Some Java tools rely on annotations in source code
@Test, @Path(“/book”),…
Annotations are for specifying program “meta-data”
They are processed by tools like JUnit, IDEs, javac, etc
Annotations are preceded by the @-symbol
Here are some annotations you may have seen before:
@Override
@SuppressWarnings(value = “unchecked”)
Developers can declare their own annotations and process these in appropriate tools
The Checker Framework offers a host of type annotations (including @NonNull and @ReadOnly) that are checked by the compiler. This can help to make programs safer.
Advantages of annotations
Let’s take @Override as an example
There are two benefits:
The compiler sees this and makes sure you are actually overriding a method when you think you are. So, if you make a mistake of misspelling a method or not correctly matching the parameters, the compiler will let you know
The code is easier to understand, as it’s clear when methods are overwritten
Some Practical Advice
Keep a link to the API doc on your desktop
Develop your programs incrementally and iteratively
start with simple versions which you enhance gradually
Write tests for non-trivial code
keep the tests and rerun them
Use a debugger or program traces when fixing errors
or insert assertions that can be enabled/ disabled
Carry out performance tests
Use annotations such as @Override
Comment your code
Follow a consistent coding style