Using interface inheritance interfaces can be merged to form a single compound interface class
There are two ways to reuse existing classes, namely, composition and inheritance. With composition (aka aggregation), you define a new class, which is composed of existing classes. With inheritance, you derive a new class based on an existing class, with modifications or extensions. Show
CompositionWe shall begin with reusing classes via composition - through examples. Composition EG. 1: The Author and Book ClassesLet's start with the Author classA class called
The Author Class (Author.java)
A Test Driver for the Author Class (TestAuthor.java)public class TestAuthor { public static void main(String[] args) { Author ahTeck = new Author("Tan Ah Teck", "", 'm'); System.out.println(ahTeck); ahTeck.setEmail(""); System.out.println(ahTeck); System.out.println("name is: " + ahTeck.getName()); System.out.println("gender is: " + ahTeck.getGender()); System.out.println("email is: " + ahTeck.getEmail()); } } A Book is written by one Author - Using an "Object" Member VariableLet's design a
The Book Class (Book.java)
A Test Driver Program for the Book Class (TestBook.java)public class TestBook { public static void main(String[] args) { Author ahTeck = new Author("Tan Ah Teck", "", 'm'); System.out.println(ahTeck); Book dummyBook = new Book("Java for dummies", ahTeck, 9.99, 99); System.out.println(dummyBook); dummyBook.setPrice(8.88); dummyBook.setQty(88); System.out.println("name is: " + dummyBook.getName()); System.out.println("price is: " + dummyBook.getPrice()); System.out.println("qty is: " + dummyBook.getQty()); System.out.println("author is: " + dummyBook.getAuthor()); System.out.println("author's name is: " + dummyBook.getAuthor().getName()); System.out.println("author's email is: " + dummyBook.getAuthor().getEmail()); System.out.println("author's gender is: " + dummyBook.getAuthor().getGender()); Book moreDummyBook = new Book("Java for more dummies", new Author("Peter Lee", "", 'm'), 19.99, 8); System.out.println(moreDummyBook); } } Notes: In this example, I used " Composition EG. 2: The Point and Line ClassesAs an example of reusing a class via composition, suppose that we have an existing class called Suppose that we need a new class called UML Notation: In UML notations, composition is represented as a diamond-head line pointing to its constituents. The Line Class via Composition (Line.java)
A Test Driver for Line Class (TestLine.java)import java.util.Arrays; public class TestLine { public static void main(String[] args) { Line l1 = new Line(1, 2, 3, 4); System.out.println(l1); Line l2 = new Line(new Point(5,6), new Point(7,8)); System.out.println(l2); l1.setBegin(new Point(11, 12)); l1.setEnd(new Point(13, 14)); System.out.println(l1); System.out.println("begin is: " + l1.getBegin()); System.out.println("end is: " + l1.getEnd()); l1.setBeginX(21); l1.setBeginY(22); l1.setEndX(23); l1.setEndY(24); System.out.println(l1); System.out.println("begin's x is: " + l1.getBeginX()); System.out.println("begin's y is: " + l1.getBeginY()); System.out.println("end's x is: " + l1.getEndX()); System.out.println("end's y is: " + l1.getEndY()); l1.setBeginXY(31, 32); l1.setEndXY(33, 34); System.out.println(l1); System.out.println("begin's x and y are: " + Arrays.toString(l1.getBeginXY())); System.out.println("end's x and y are: " + Arrays.toString(l1.getEndXY())); System.out.printf("length is: %.2f%n", l1.getLength()); } } Exercise: Try writing these more complex methods for the public double getGradient() public double distance(int x, int y) public double distance(Point p) public boolen intersects(Line another) Composition EG. 3: The Point and Circle ClassesSuppose that we have an existing class called A class called It contains:
The Circle class (Circle.java)
A Test Driver for the Circle Class (TestCircle.java)
ExercisesLINK TO EXERCISES InheritanceIn OOP, we often organize classes in hierarchy to avoid duplication and reduce redundancy. The classes in the lower hierarchy inherit all the variables (static attributes) and methods (dynamic behaviors) from the higher hierarchies. A class in the lower hierarchy is called a subclass (or derived, child, extended class). A class in the upper hierarchy is called a superclass (or base, parent class). By pulling out all the common variables and methods into the superclasses, and leave the specialized variables and methods in the subclasses, redundancy can be greatly reduced or eliminated as these common variables and methods do not need to be repeated in all the subclasses. For example, A subclass inherits all the variables and methods from its superclasses, including its immediate parent as well as all the ancestors. It is important to note that a subclass is not a "subset" of a superclass. In contrast, subclass is a "superset" of a superclass. It is because a subclass inherits all the variables and methods of the superclass; in addition, it extends the superclass by providing more variables and methods. In Java, you define a subclass using the keyword " class Goalkeeper extends SoccerPlayer {......} class MyApplet extends java.applet.Applet {.....} class Cylinder extends Circle {......} UML Notation: The UML notation for inheritance is a solid line with a hollow arrowhead leading from the subclass to its superclass. By convention, superclass is drawn on top of its subclasses as shown. Inheritance EG. 1: The Circle and Cylinder ClassesIn this example, we derive a subclass called Circle.java (Re-produced)public class Circle { private double radius; private String color; public Circle() { this.radius = 1.0; this.color = "red"; System.out.println("Construced a Circle with Circle()"); } public Circle(double radius) { this.radius = radius; this.color = "red"; System.out.println("Construced a Circle with Circle(radius)"); } public Circle(double radius, String color) { this.radius = radius; this.color = color; System.out.println("Construced a Circle with Circle(radius, color)"); } public double getRadius() { return this.radius; } public String getColor() { return this.color; } public void setRadius(double radius) { this.radius = radius; } public void setColor(String color) { this.color = color; } public String toString() { return "Circle[radius=" + radius + ",color=" + color + "]"; } public double getArea() { return radius * radius * Math.PI; } } Cylinder.javapublic class Cylinder extends Circle { private double height; public Cylinder() { super(); this.height = 1.0; System.out.println("Constructed a Cylinder with Cylinder()"); } public Cylinder(double height) { super(); this.height = height; System.out.println("Constructed a Cylinder with Cylinder(height)"); } public Cylinder(double height, double radius) { super(radius); this.height = height; System.out.println("Constructed a Cylinder with Cylinder(height, radius)"); } public Cylinder(double height, double radius, String color) { super(radius, color); this.height = height; System.out.println("Constructed a Cylinder with Cylinder(height, radius, color)"); } public double getHeight() { return this.height; } public void setHeight(double height) { this.height = height; } public String toString() { return "This is a Cylinder"; } } A Test Drive for the Cylinder Class (TestCylinder.java)public class TestCylinder { public static void main(String[] args) { Cylinder cy1 = new Cylinder(); System.out.println("Radius is " + cy1.getRadius() + ", Height is " + cy1.getHeight() + ", Color is " + cy1.getColor() + ", Base area is " + cy1.getArea() + ", Volume is " + cy1.getVolume()); Cylinder cy2 = new Cylinder(5.0, 2.0); System.out.println("Radius is " + cy2.getRadius() + ", Height is " + cy2.getHeight() + ", Color is " + cy2.getColor() + ", Base area is " + cy2.getArea() + ", Volume is " + cy2.getVolume()); } } Keep the " Method Overriding & Variable HidingA subclass inherits all the member variables and methods from its superclasses (the immediate parent and all its ancestors). It can use the inherited methods and variables as they are. It may also override an inherited method by providing its own version, or hide an inherited variable by defining a variable of the same name. For example, the inherited method
If But if you override the
Annotation @Override (JDK 1.5)The "
Annotations are not programming constructs. They have no effect on the program output. It is only used by the compiler, discarded after compilation, and not used by the runtime. Keyword "super"Recall that inside a class definition, you can use the keyword The keyword More on ConstructorsRecall that the subclass inherits all the variables and methods from its superclasses. Nonetheless, the subclass does not inherit the constructors of its superclasses. Each class in Java defines its own constructors. In the body of a constructor, you can use Default no-arg ConstructorIf no constructor is defined in a class, Java compiler automatically create
a no-argument (no-arg) constructor, that simply issues a public ClassName () { super(); } Take note that:
Single InheritanceJava does not support multiple inheritance (C++ does). Multiple inheritance permits a subclass to have more than one direct superclasses. This has a serious drawback if the superclasses have conflicting implementation for the same method. In Java, each subclass can have one and only one direct superclass, i.e., single inheritance. On the other hand, a superclass can have many subclasses. Common Root Class - java.lang.ObjectJava adopts a so-called common-root approach. All Java classes are derived from a common root class called Inheritance EG. 2: The Point2D and Point3D ClassesThe Superclass Point2D.javapublic class Point2D { private int x, y; public Point2D() { this.x = 0; this.y = 0; } public Point2D(int x, int y) { this.x = x; this.y = y; } public int getX() { return this.x; } public void setX(int x) { this.x = x; } public int getY() { return this.y; } public void setY(int y) { this.y = y; } @Override public String toString() { return "(" + this.x + "," + this.y + ")"; } } The Subclass Point3D.java
A Test Driver for Point2D and Point3D Classes (TestPoint2DPoint3D.java)
Inheritance EG. 3: Superclass Person and its SubclassesSuppose that we are required to model students and teachers in our application. We can define a superclass called We design the classes as follows. The Superclass Person.javapublic class Person { private String name, address; public Person(String name, String address) { this.name = name; this.address = address; } public String getName() { return name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return name + "(" + address + ")"; } } The Subclass Student.javapublic class Student extends Person { private int numCourses; private String[] courses; private int[] grades; private static final int MAX_COURSES = 30; public Student(String name, String address) { super(name, address); numCourses = 0; courses = new String[MAX_COURSES]; grades = new int[MAX_COURSES]; } @Override public String toString() { return "Student: " + super.toString(); } public void addCourseGrade(String course, int grade) { courses[numCourses] = course; grades[numCourses] = grade; ++numCourses; } public void printGrades() { System.out.print(this); for (int i = 0; i < numCourses; ++i) { System.out.print(" " + courses[i] + ":" + grades[i]); } System.out.println(); } public double getAverageGrade() { int sum = 0; for (int i = 0; i < numCourses; i++ ) { sum += grades[i]; } return (double)sum/numCourses; } } The Subclass Teacher.javapublic class Teacher extends Person { private int numCourses; private String[] courses; private static final int MAX_COURSES = 5; public Teacher(String name, String address) { super(name, address); numCourses = 0; courses = new String[MAX_COURSES]; } @Override public String toString() { return "Teacher: " + super.toString(); } public boolean addCourse(String course) { for (int i = 0; i < numCourses; i++) { if (courses[i].equals(course)) return false; } courses[numCourses] = course; numCourses++; return true; } public boolean removeCourse(String course) { boolean found = false; int courseIndex = -1; for (int i = 0; i < numCourses; i++) { if (courses[i].equals(course)) { courseIndex = i; found = true; break; } } if (found) { for (int i = courseIndex; i < numCourses-1; i++) { courses[i] = courses[i+1]; } numCourses--; return true; } else { return false; } } } A Test Driver (TestPerson.java)public class TestPerson { public static void main(String[] args) { Student s1 = new Student("Tan Ah Teck", "1 Happy Ave"); s1.addCourseGrade("IM101", 97); s1.addCourseGrade("IM102", 68); s1.printGrades(); System.out.println("Average is " + s1.getAverageGrade()); Teacher t1 = new Teacher("Paul Tan", "8 sunset way"); System.out.println(t1); String[] courses = {"IM101", "IM102", "IM101"}; for (String course: courses) { if (t1.addCourse(course)) { System.out.println(course + " added"); } else { System.out.println(course + " cannot be added"); } } for (String course: courses) { if (t1.removeCourse(course)) { System.out.println(course + " removed"); } else { System.out.println(course + " cannot be removed"); } } } } ExercisesLINK TO EXERCISES Composition vs. Inheritance"A line is composed of 2 points" vs. "A line is a point extended by another point"Recall that there are two ways of reusing existing classes: composition and inheritance. We have seen that a A The Superclass Point.javaAs above. The Subclass LineSub.java
A Test Driver (TestLineSub.java)
Notes: This is the same test driver used in the earlier example on composition, except change in classname. Study both versions of the Line class ( Rule of Thumb: Use composition if possible, before considering inheritance. Use inheritance only if there is a clear hierarchical relationship between classes. ExercisesLINK TO EXERCISES ON COMPOSITION VS INHERITANCE PolymorphismThe word "polymorphism" means "many forms". It comes from Greek word "poly" (means many) and "morphos" (means form). For examples, in chemistry, carbon exhibits polymorphism because it can be found in more than one form: graphite and diamond. But, each of the form has it own distinct properties (and price). SubstitutabilityA subclass possesses all the attributes and operations of its superclass (because a subclass inherited all attributes and operations from its superclass). This means that a subclass object can do whatever its superclass can do. As a result, we can substitute a subclass instance when a superclass instance is expected, and everything shall work fine. This is called substitutability. In our earlier example of Circle.javapublic class Circle { private double radius; public Circle(double radius) { this.radius = radius; } public double getRadius() { return this.radius; } public double getArea() { return radius * radius * Math.PI; } public String toString() { return "Circle[radius=" + radius + "]"; } } Cylinder.javapublic class Cylinder extends Circle { private double height; public Cylinder(double height, double radius) { super(radius); this.height = height; } public double getHeight() { return this.height; } public double getVolumne() { return super.getArea() * height; } @Override public double getArea() { return 2.0 * Math.PI * getRadius() * height; } @Override public String toString() { return "Cylinder[height=" + height + "," + super.toString() + "]"; } } Via substitutability, we can create an instance of Circle c1 = new Cylinder(1.1, 2.2); You can invoke all the methods defined in the System.out.println(c1.getRadius());
This is because a subclass instance possesses all the properties of its superclass. However, you CANNOT invoke methods
defined in the c1.getHeight(); //compilation error: cannot find symbol method getHeight() c1.getVolume(); //compilation error: cannot find symbol method getVolume() This is because
System.out.println(c1.toString()); Cylinder[height=1.1,Circle[radius=2.2]] System.out.println(c1.getArea()); Summary
Polymorphism EG. 1: Shape and its SubclassesPolymorphism is very powerful in OOP to separate the interface and implementation so as to allow the programmer to program at the interface in the design of a complex system. Consider the following example. Suppose that our program uses many kinds of shapes, such as triangle, rectangle and so on. We should design a superclass called
Superclass Shape.javapublic class Shape { private String color; public Shape (String color) { this.color = color; } @Override public String toString() { return "Shape[color=" + color + "]"; } public double getArea() { System.err.println("Shape unknown! Cannot compute area!"); return 0; } } Take note that we have a problem writing the
We can then derive subclasses, such as Subclass Rectangle.javapublic class Rectangle extends Shape { private int length, width; public Rectangle(String color, int length, int width) { super(color); this.length = length; this.width = width; } @Override public String toString() { return "Rectangle[length=" + length + ",width=" + width + "," + super.toString() + "]"; } @Override public double getArea() { return length*width; } } Subclass Triangle.javapublic class Triangle extends Shape { private int base, height; public Triangle(String color, int base, int height) { super(color); this.base = base; this.height = height; } @Override public String toString() { return "Triangle[base=" + base + ",height=" + height + "," + super.toString() + "]"; } @Override public double getArea() { return 0.5*base*height; } } The subclasses override the A Test Driver (TestShape.java)In our application, we could create references of public class TestShape { public static void main(String[] args) { Shape s1 = new Rectangle("red", 4, 5); System.out.println(s1); Rectangle[length=4,width=5,Shape[color=red]] System.out.println("Area is " + s1.getArea()); Shape s2 = new Triangle("blue", 4, 5); System.out.println(s2); Triangle[base=4,height=5,Shape[color=blue]] System.out.println("Area is " + s2.getArea()); } } The beauty of this code is that all the references are from the superclass (i.e., programming at the interface level). You could instantiate different subclass instance, and the code still works. You could extend
your program easily by adding in more subclasses, such as Nonetheless, the above definition of public class TestShape { public static void main(String[] args) { Shape s3 = new Shape("green"); System.out.println(s3); System.out.println("Area is " + s3.getArea()); } } This is because the Polymorphism EG. 2: Monster and its SubclassesPolymorphism is a powerful mechanism in OOP to separate the interface and implementation so as to allow the programmer to program at the interface in the design of a complex system. For example, in
our game app, we have many types of monsters that can attack. We shall design a superclass called Superclass Monster.javapublic class Monster { private String name; public Monster(String name) { this.name = name; } public String attack() { return "!^_&^$@+%$* I don't know how to attack!"; } } Subclass FireMonster.javapublic class FireMonster extends Monster { public FireMonster(String name) { super(name); } @Override public String attack() { return "Attack with fire!"; } } Subclass WaterMonster.javapublic class WaterMonster extends Monster { public WaterMonster(String name) { super(name); } @Override public String attack() { return "Attack with water!"; } } Subclass StoneMonster.javapublic class StoneMonster extends Monster { public StoneMonster(String name) { super(name); } @Override public String attack() { return "Attack with stones!"; } } A Test Driver TestMonster.javapublic class TestMonster { public static void main(String[] args) { Monster m1 = new FireMonster("r2u2"); Monster m2 = new WaterMonster("u2r2"); Monster m3 = new StoneMonster("r2r2"); System.out.println(m1.attack()); System.out.println(m2.attack()); System.out.println(m3.attack()); m1 = new StoneMonster("a2b2"); System.out.println(m1.attack()); Monster m4 = new Monster("u2u2"); System.out.println(m4.attack()); } } Upcasting & DowncastingUpcasting a Subclass Instance to a Superclass ReferenceSubstituting a subclass instance for its superclass is called "upcasting". This is because, in a UML class diagram, subclass is often drawn below its superclass. Upcasting is always safe because a subclass instance possesses all the properties of its superclass and can do whatever its superclass can do. The compiler checks for valid upcasting and issues error "incompatible types" otherwise. For example, Circle c1 = new Cylinder(1.1, 2.2); Circle c2 = new String(); Downcasting a Substituted Reference to Its Original ClassYou can revert a substituted instance back to a subclass reference. This is called "downcasting". For example, Circle c1 = new Cylinder(1.1, 2.2); Cylinder cy1 = (Cylinder) c1; Downcasting requires explicit type casting operator in the form of prefix operator
Another Example on Upcasting and Downcastingpublic class A { public A() { System.out.println("Constructed an instance of A"); } @Override public String toString() { return "This is A"; } } public class B extends A { public B() { super(); System.out.println("Constructed an instance of B"); } @Override public String toString() { return "This is B"; } } public class C extends B { public C() { super(); System.out.println("Constructed an instance of C"); } @Override public String toString() { return "This is C"; } } The following program tests the upcasting an downcasting (refer to the above instance diagram): public class TestCasting { public static void main(String[] args) { A a1 = new C(); Constructed an instance of A System.out.println(a1); B b1 = (B)a1; System.out.println(b1); C c1 = (C)b1; System.out.println(c1); A a2 = new B(); System.out.println(a2); B b2 = (B)a2; C c2 = (C)a2; compilation okay, but runtime error: } } Casting OperatorCompiler may not be able to detect error in explicit cast, which will be detected only at runtime. For example, Circle c1 = new Circle(5);
Point p1 = new Point();
c1 = p1; compilation error: incompatible types (Point is not a subclass of Circle)
c1 = (Circle)p1; The "instanceof" OperatorJava provides a binary operator called anObject instanceof aClass Circle c1 = new Circle(); System.out.println(c1 instanceof Circle); if (c1 instanceof Circle) { ...... } An instance of subclass is also an instance of its superclass. For example, Circle c1 = new Circle(1.1); Cylinder cy1 = new Cylinder(2.2, 3.3); System.out.println(c1 instanceof Circle); System.out.println(c1 instanceof Cylinder); System.out.println(cy1 instanceof Cylinder); System.out.println(cy1 instanceof Circle); Circle c2 = new Cylinder(4.4, 5.5); System.out.println(c2 instanceof Circle); System.out.println(c2 instanceof Cylinder); Summary of Polymorphism
ExercisesLINK TO EXERCISES Abstract Classes & InterfacesThe abstract Method and abstract classIn the above examples of An For example, in the abstract public class Shape { ...... ...... abstract public double getArea(); abstract public double getPerimeter(); abstract public void draw(); } Implementation of these methods is NOT possible in the A class containing one or more UML Notation: Abstract Class EG. 1: Shape and its SubclassesLet us rewrite our The abstract Superclass Shape.javaabstract public class Shape { private String color; public Shape (String color) { this.color = color; } @Override public String toString() { return "Shape[color=" + color + "]"; } abstract public double getArea(); } An To use an This property of
the public class TestShape {
public static void main(String[] args) {
Shape s1 = new Rectangle("red", 4, 5);
System.out.println(s1);
System.out.println("Area is " + s1.getArea());
Shape s2 = new Triangle("blue", 4, 5);
System.out.println(s2);
System.out.println("Area is " + s2.getArea());
Shape s3 = new Shape("green");
}
} In summary, an Coupled with polymorphism, you can upcast subclass
instances to Rule of Thumb: Program at the interface, not at the implementation. (That is, make references at the superclass; substitute with subclass instances; and invoke methods defined in the superclass only.) Notes:
Abstract Class EG. 2: MonsterWe shall define the superclass abstract public class Monster { private String name; public Monster(String name) { this.name = name; } abstract public String attack(); } The Java's interfaceA Java (JDK 8 introduces default and static methods in the interface. JDK 9 introduces private methods in the interface. These will not be covered in this article.) Similar to an Unlike a normal class, where you use the keyword " An interface is a contract for what the classes can do. It, however, does not specify how the classes should do it. An interface provides a form, a protocol, a standard, a contract, a specification, a set of rules, an interface, for all objects that implement it. It is a specification and rules that any object implementing it agrees to follow. In Java, Interface Naming Convention: Use an adjective (typically ends with " Interface EG. 1: Shape Interface and its ImplementationsWe can re-write the UML Notations: Abstract classes, Interfaces and abstract methods are shown in italics. Implementation of interface is marked by a dash-arrow leading from the subclasses to the interface. public interface Shape { double getArea(); } public class Rectangle implements Shape {
private int length, width;
public Rectangle(int length, int width) {
this.length = length;
this.width = width;
}
@Override
public String toString() {
return "Rectangle[length=" + length + ",width=" + width + "]";
}
@Override
public double getArea() {
return length * width;
}
} public class Triangle implements Shape {
private int base, height;
public Triangle(int base, int height) {
this.base = base;
this.height = height;
}
@Override
public String toString() {
return "Triangle[base=" + base + ",height=" + height + "]";
}
@Override
public double getArea() {
return 0.5 * base * height;
}
} A test driver is as follows: public class TestShape { public static void main(String[] args) { Shape s1 = new Rectangle(1, 2); System.out.println(s1); System.out.println("Area is " + s1.getArea()); Shape s2 = new Triangle(3, 4); System.out.println(s2); System.out.println("Area is " + s2.getArea()); } } Interface EG. 2: Movable Interface and its ImplementationsSuppose that our application involves many objects that can move. We could define an interface called Interface Moveable.javapublic interface Movable {
public void moveUp();
public void moveDown();
public void moveLeft();
public void moveRight();
}
Similar to an MovablePoint.javaTo derive subclasses from an public class MovablePoint implements Movable { private int x, y; public MovablePoint(int x, int y) { this.x = x; this.y = y; } @Override public String toString() { return "(" + x + "," + y + ")"; } @Override public void moveUp() { y--; } @Override public void moveDown() { y++; } @Override public void moveLeft() { x--; } @Override public void moveRight() { x++; } } Other classes in the application can similarly implement the TestMovable.javaWe can also upcast subclass instances to the
public class TestMovable { public static void main(String[] args) { MovablePoint p1 = new MovablePoint(1, 2); System.out.println(p1); p1.moveDown(); System.out.println(p1); p1.moveRight(); System.out.println(p1); Movable p2 = new MovablePoint(3, 4); p2.moveUp(); System.out.println(p2); MovablePoint p3 = (MovablePoint)p2; System.out.println(p3); } } Implementing Multiple InterfacesAs mentioned, Java supports only single inheritance. That is, a subclass can be derived from one and only one superclass. Java does not support multiple inheritance to avoid inheriting conflicting properties from multiple superclasses. Multiple inheritance, however, does have its place in programming. A subclass, however, can implement more than one interfaces. This is permitted in Java as an interface merely defines the abstract methods without the actual implementations and less likely leads to inheriting conflicting properties from multiple interfaces. In other words, Java indirectly supports multiple inheritances via implementing multiple interfaces. For example, public class Circle extends Shape implements Movable, Adjustable { ....... } interface Formal SyntaxThe formal syntax for declaring interface is: [public|protected|package] interface interfaceName [extends superInterfaceName] { static final ...; ... } All methods in an interface shall be All fields shall be An UML Notation: The UML notation uses a solid-line arrow linking the subclass to a concrete or abstract superclass, and dashed-line arrow to an interface as illustrated. Abstract class and abstract method are shown in italics. Why interfaces?An interface is a contract (or a protocol, or a common understanding) of what the classes can do. When a class implements a certain interface, it promises to provide implementation to all the abstract methods declared in the interface. Interface defines a set of common behaviors. The classes implement the interface agree to these behaviors and provide their own implementation to the behaviors. This allows you to program at the interface, instead of the actual implementation. One of the main usage of interface is provide a communication contract between two objects. If you know a class implements an interface, then you know that class contains concrete implementations of the methods declared in that interface, and you are guaranteed to be able to invoke these methods safely. In other words, two objects can communicate based on the contract defined in the interface, instead of their specific implementation. Secondly, Java does not support multiple inheritance (whereas C++ does). Multiple inheritance permits you to derive a subclass from more than one direct superclass. This poses a problem if two direct superclasses have conflicting implementations. (Which one to follow in the subclass?). However, multiple inheritance does have its place. Java does this by permitting you to "implements" more than one interfaces (but you can only "extends" from a single superclass). Since interfaces contain only abstract methods without actual implementation, no conflict can arise among the multiple interfaces. (Interface can hold constants but is not recommended. If a subclass implements two interfaces with conflicting constants, the compiler will flag out a compilation error.) Interface vs. Abstract SuperclassWhich is a better design: interface or abstract superclass? There is no clear answer. Use abstract superclass if there is a clear class hierarchy. Abstract class can contain partial implementation (such as instance variables and methods). Interface cannot contain any implementation, but merely defines the behaviors. As an example, Java's thread can be built using interface ExercisesLINK TO EXERCISES ON POLYMORPHISM, ABSTRACT CLASSES AND INTERFACES (Advanced) Dynamic Binding or Late BindingWe often treat an object not as its own type, but as its base type (superclass or interface). This allows you to write codes that do not depends on a specific implementation type. In the This, however, poses a new problem. The compiler cannot know at compile time precisely which piece of codes is going to be executed at run-time (e.g., In the procedural language like C, the compiler generates a call to a specific function name, and the linkage editor resolves this call to the absolute address of the code to be executed at run-time. This mechanism is called static binding (or early binding). To support polymorphism, object-oriented language uses a different mechanism called dynamic binding (or late-binding or run-time binding). When a method is invoked, the code to be executed is only determined at run-time. During the compilation, the compiler checks whether the method exists and performs type check on the arguments and return type, but does not know which piece of codes to execute at run-time. When a message is sent to an object to invoke a method, the object figures out which piece of codes to execute at run-time. Although dynamic binding resolves the problem in supporting polymorphism, it poses another new problem. The compiler is unable to check whether the type casting operator is safe. It can only be checked during runtime (which throws a JDK 1.5 introduces a new feature called generics to tackle this issue. We shall discuss this problem and generics in details in the later chapter. ExercisesLINK TO EXERCISES (Advanced) Object-Oriented Design IssuesEncapsulation, Coupling & CohesionIn OO Design, it is desirable to design classes that are tightly encapsulated, loosely coupled and highly cohesive, so that the classes are easy to maintain and suitable for re-use. Encapsulation refers to keeping the data and method inside a class such users do not access the data directly but via the [TODO] Example:
Time class with private variables hour (0-23), minute (0-59) and second (0-59); getters and setters (throws Information Hiding: Another key benefit of tight encapsulation is information hiding, which means that the users are not aware (and do not need to be aware) of how the data is stored internally. The benefit of tight encapsulation out-weights the overhead needed in additional method calls. Coupling refers to the degree to which one class relies on knowledge of the internals of another class. Tight coupling is undesirable because if one class changes its internal representations, all the other tightly-coupled classes need to be rewritten. [TODO] Example: A class uses Time and relies on the variables hour, minute and second. Clearly, Loose Coupling is often associated with tight encapsulation. For example, well-defined public method for accessing the data, instead of directly access the data. Cohesion refers to the degree to which a class or method resists being broken down into smaller pieces. High degree of cohesion is desirable. Each class shall be designed to model a single entity with its focused set of responsibilities and perform a collection of closely related tasks; and each method shall accomplish a single task. Low cohesion classes are hard to maintain and re-use. [TODO] Example of low cohesion: Book and Author in one class, or Car and Driver in one class. Again, high cohesion is associated with loose coupling. This is because a highly cohesive class has fewer (or minimal) interactions with other classes. "Is-a" vs. "has-a" relationships"Is-a" relationship: A subclass object processes all the data and methods from its superclass (and it could have more). We can say that a subclass object is-a superclass object (is more than a superclass object). Refer to "polymorphism". "has-a" relationship: In composition, a class contains references to other classes, which is known as "has-a" relationship. You can use "is-a" and 'has-a" to test whether to design the classes using inheritance or composition. Program at the interface specification, not the implementationRefer to polymorphism LINK TO JAVA REFERENCES & RESOURCES Can an interface inherit another interface?Interfaces can inherit from one or more interfaces. The derived interface inherits the members from its base interfaces. A class that implements a derived interface must implement all members in the derived interface, including all members of the derived interface's base interfaces.
Can we inherit one interface to another interface in Java?An interface cannot implement another interface in Java. An interface in Java is essentially a special kind of class. Like classes, the interface contains methods and variables. Unlike classes, interfaces are always completely abstract.
How many interface can a class inherit?Implementing Multiple Interfaces. Although classes can inherit only one class, they can implement multiple interfaces. In the example above, we notice the use of the keyword implements to inherit from an interface.
What is the relation between interface and inheritance?Inheritance is the mechanism in java by which one class is allowed to inherit the features of another class. Interface is the blueprint of the class. It specifies what a class must do and not how.
|