Abstract Classes We often define classes as subclasses
Abstract Classes • We often define classes as subclasses of other classes – First, define the superclass – In some cases, a superclass may not have enough specificity to allow for an implementation of one or more methods • a Geometric. Object class might be the parent for Circle and Rectangle • compute. Area and compute. Perimeter are two useful methods for these subclasses but Geometric. Object is too generic of a class to be able to implement these methods on • yet we want to define these methods in Geometric. Object so they are inherited by Circle and Rectangle • recall we did something similar when implement Vehicle so that other Vehicle subclasses could inherit get. Battle. Utility • An abstract method is a definition for a method without an implementation • An abstract class is a class that contains at least one abstract method
Abstract Classes • You cannot instantiate an object of an abstract class (although you can declare a variable to be of that type for polymorphism) – Here we see obj is a Geometric. Object – We cannot instantiate obj to be a Geometric. Object but we can instantiate it to be a Circle or a Rectangle • • Geometric. Object obj; obj = new Circle(10); … obj = new Rectangle(5, 10); • The reason for having abstract classes is first to provide a “placeholder” that informs programmers of what needs to be implemented in a subclass and second to provide a common basis for all subclasses of this superclass
Defining an Abstract Class • Add the word abstract to the class header – public abstract class Name • You can define instance data, constants and methods as normal – You can also implement at least one abstract method (you do not have to have abstract methods in an abstract class) • To declare an abstract method – Place the word abstract in the method header – In place of the method’s implementation {…} just put a ; • public abstract void foo(…); – The method’s profile (return type, parameters) must be exactly as you expect subclasses to implement them • you must override an abstract method, it can not be overloaded • you cannot make an abstract method static • The example that follows is the UML for Geometric. Object, Circle and Rectangle
Some Comments • Abstract methods cannot be placed in non-abstract classes – If a class extends an abstract class, it must either implement all inherited abstract methods or itself also be abstract – Recall that you cannot instantiate an object of an abstract class • An abstract class does not need to contain abstract methods – You would only do this if the superclass was merely a base for other classes to inherit from • A subclass can override a parent class’ method and make it abstract – in which case the subclass would have to be abstract – Here the superclass is not [necessarily] abstract while the subclass is – This is unusual but allows you to “shut off” inheritance of a superclass implementation of a method
Example: Revising Vehicle public abstract class Vehicle { protected String type; protected boolean heavily. Armored; protected int capacity; protected int speed; protected int armament; public public } As before Vehicle(String t) {. . . } String get. Type() {. . . } boolean get. Heavily. Armored() {. . . } int get. Capcity() {. . . } int get. Speed() {. . . } int get. Armament() {. . . } String to. String() {. . . } abstract int get. Battle. Utility (boolean night, boolean rough. Terrain, boolean need. Ground. Support, boolean need. Heavy. Arms, boolean anti. Aircraft. Guns, boolean equipment, int distance); abstract and no implementation
Another Example • We want to define a deck of cards – There are different types of cards • Ordinary cards which are shuffled randomly • Magic cards which are not shuffled randomly • Stacked decks • We will define Card. Class to represent an abstract deck of cards – This class will create instance data of the deck itself (an array of ints which indicate the order of the cards currently) and whether a particular card has been dealt yet (a boolean array) – Non-abstract methods will be a constructor and accessors to get a card and determine if a card has been dealt – But the shuffle method will be abstract based on the subclass • We will then implement a subclass, Playing. Cards to be a deck of ordinary playing cards – The shuffle method is implemented as a simple random generation of the sequence of cards (numbers 0 -51)
public abstract class Card. Class { protected int[] deck; protected boolean[] dealt; public Card. Class() { deck=new int[52]; dealt=new boolean[52]; for(int i=0; i<52; i++) dealt[i]=false; } public boolean dealt. Yet(int index) throws Index. Out. Of. Bounds. Exception { if(index<52) return dealt[index]; else throw new Index. Out. Of. Bounds. Exception(index + " is too large for the size of this deck"); } public int get. Card(int index) throws Index. Out. Of. Bounds. Exception { if(index<52) return deck[index]; else throw new Index. Out. Of. Bounds. Exception(index + " is too large for the size of this deck"); } public abstract void shuffle(); }
import java. util. *; public class Playing. Cards extends Card. Class protected Random g; public Playing. Cards() { super(); g=new Random(); } { public void shuffle() { boolean[] order=new boolean[52]; int i, temp; for(i=0; i<52; i++) order[i]=false; for(i=0; i<52; i++) { do { temp=g. next. Int(52); }while(order[temp]); order[temp]=true; deck[i]=temp; } } }
Arrays of an Abstract Class • You cannot instantiate an abstract class • But you can create an array of an abstract class and instantiate the individual array elements to be subclasses – Assume we have our previously described Geometric. Shape, Circle and Rectangle classes – Geometric. Shape[ ] shapes = new Geometric. Shape[10]; • This is legal – shapes[0] = new Circle(10); – shapes[1] = new Rectangle(5, 10); • These are legal – Shapes[2] = new Geometric. Shape( ); • This is illegal because Geometric. Shape is abstract
Interfaces • Java does not permit multiple inheritance (inheriting from multiple classes) but it does provide interfaces – In a way, interfaces are a form of multiple inheritance • An interface is like an abstract class in that it defines a portion of a class but does not implement it – The interface class contains constants and abstract methods only, no implemented methods and no instance data – By implementing an interface, like extending an abstract class, you are completing the class – The advantage of implementing an interface is that you are stating that the given class will act a certain way so that other classes can expect a particular functionality • for instance, you might implement Comparable indicating that the class will implement compares. To
Implementing an Interface • You add to the class header implements Interface. Name • Now you must override the abstract method(s) defined in the Interface – If you do not override all of the abstract methods, you will receive compilation errors – You cannot instantiate an object of a type of Interface class because of the abstract methods (just as you cannot instantiate the object of an abstract class), all you can do is implement the Interface class in your own class • One of the most significant types of Interfaces is the class of GUI handlers that will respond when a GUI component, mouse, or keyboard action occurs – We will look at this later in the semester – Here, we will concentrate on two of the most significant interfaces: Comparable and Cloneable
Implementing Comparable • Comparable is an interface class meaning that as a class, it is defined as an interface which means that you cannot instantiate an object of this type • You also will (most likely) not extend Comparable but instead implement Comparable • The reason for the Comparable interface is to indicate that a class that you are implementing will have the ability to directly compare two instances of this type – recall that the relational operators (<, >, etc) are not available for objects, only for primitive types – so instead, we will implement Comparable which requires that we implement a method called compare. To (recall Strings have this method, the reason being that Strings implement Comparable requiring this method be implemented) • How do you compare two objects? Depends on the type of object – we look at a couple of examples coming up
Continued • To implement Comparable, do the following – Add implements Comparable to the class header • As in public class My. Class extends Your. Class implements Comparable – Add a compare. To method with the following profile • public int compare. To(Object o) {…} • Notice that your compare. To is receiving an Object, not a My. Class object, why? Because this is how compare. To appears as an abstract method in the Comparable class • If you do not match this profile exactly then you are not overriding the abstract compare. To but instead overloading it, and so compilation will fail • compare. To will return an int value: negative if this object is less than the object passed to compare. To, positive if this object is greater and 0 if the two objects are deemed equivalent
Examples • Here, we make the Circle class (introduced earlier in the semester) Comparable – Circles will be compared based on their radius public class Circle extends Geometric. Object implements Comparable {. . . // as before public int compare. To(Object o) { if(radius>((Circle)o). get. Radius( )) return 1; else if(radius<((Circle)o). get. Radius( )) return -1; else return 0; } } • As an alternative to using (Object o) and downcasting as shown above, we can implement Comparable<Circle> employing generics – see the next example
Another Example: Comparing Names public class Name implements Comparable<Name> private String first, middle, last; { public Name(String f, String m, String l) { first=f; middle=m; last=l; } public String get. First() {return first; } public String get. Middle() {return middle; } public String get. Last() {return last; } public int compare. To(Name n) { if(last. compare. To(n. get. Last())>0) return 1; else if(last. compare. To(n. get. Last())<0) return -1; else if(first. compare. To(n. get. First())>0) return 1; else if(first. compare. To(n. get. First())<0) return -1; else if(middle. compare. To(n. get. Middle())>0) return 1; else if(middle. compare. To(n. get. Middle())<0) return -1; else return 0; } }
Creating An Interface Class • You can create your own interface classes by substituting the modifier interface in place of class – public interface Interface. Name • The Interface’s definition will consist solely of constants and abstract method definitions – The abstract methods have no body (no {…}) and end with a ; • Example: we have a class called Instrument to define various types of musical instruments – We want to define an interface class called Playable which, if implemented, will indicate how to play that instrument – public interface Playable { public abstract String how. To. Play( ); } – Now, if we implement Playable, we have to define a how. To. Play method which returns a String
Implementing Playable public class Drums extends Instrument implements Playable { private int num. Drums; public Drums(int size) { super(“Drums”); // assume Instrument’s constructor ass num. Drums=size; // the var type to this String } public Drums( ) { super(“Drums”); num. Drums=0; } public String how. To. Play( ) { return “Strike drums with drumsticks or mallets”; } } public class Guitar extends private int num. Strings; // constructors similar public String how. To. Play( return “Strum strings } } Instrument implements Playable to Drums ) { with pick or fingers”; {
Implementing Cloneable • Consider the following code – Some. Object o 1, o 2; – o 1=new Some. Object(…); – o 2=o 1; • In this code, o 1 points at an object in heap memory that has been instantiated with the new operator, but what about o 2? – Is o 2 another object with the same values as o 1 or is it the same object? It is the same • If we instead want to “clone” o 1 into o 2 we would create another object, have o 2 point at the new object in the heap, and set all of its values to be the same as o 1 • In this way, we have two different objects which are equivalent rather than two reference variables which are equal in that they are pointing at the same object • We accomplish this by implementing Cloneable which requires implementing a clone method
Cloneable Interface • Unlike Comparable, the Cloneable interface is empty – there is no abstract method defined – You must still implement the clone method • According to the textbook, the abstract method for clone is defined as follows – protected native Object clone( ) throws Clone. Not. Supported. Exception; – native means that the actual implementation for copying data is left up to the JVM, not the compiler – Notice that clone returns an Object so you will have to cast the returned object appropriately – You must either add the “throws Clone…” to your method header or handle it in a try-catch block when you attempt to clone an object of the class you have implemented as cloneable
Example: Person Class public class Person implements Cloneable { private String name; private int age, height, weight; private boolean is. Married; private Person spouse; public Person() { age=0; name="unknown"; is. Married=false; spouse=null; } public Person(String n, int a, int h, int w) age=a; name=n; height=h; weight=w; is. Married=false; spouse=null; } {
public Person(String n, int a, int h, int w, Person s) { age=a; name=n; height=h; weight=w; is. Married=true; spouse=s; } public void gets. Married(Person s) { if(!is. Married) { is. Married=true; spouse=s; } else System. out. println(name + " cannot marry " + s. get. Name() + " until " + name + " gets a divorce!"); } public public String get. Name() {return name; } int get. Age() {return age; } int get. Height() {return height; } int get. Weight() {return weight; } String get. Spouse. Name() {if(is. Married) return spouse. get. Name(); else return "not married"; }
public public void set. Name(String n) { if(name==null) name=n; } void set. Age(int a) { if(age==0) age=a; } void set. Height(int h) { if(height==0) height=h; } void set. Weight(int w) { if(weight==0) weight=w; } void set. Married(Person s) { if(!is. Married) { is. Married=true; spouse=s; } } } public Object clone() throws Clone. Not. Supported. Exception { Person p=(Person)super. clone(); p. set. Name(name); p. set. Age(age); p. set. Height(height); p. set. Weight(weight); p. set. Married(spouse); return p; } // ends the class
People Class public class People { public static void main(String[] args) throws Clone. Not. Supported. Exception { Person p 1=new Person(); Person p 2=new Person("Frank Zappa", 53, 69, 158); Person p 3=new Person("Gail Sloatman", 50, 63, 110); p 2. gets. Married(p 3); p 3. gets. Married(p 2); p 1=(Person)p 2. clone(); System. out. println(p 1. get. Name() + " " + p 1. get. Age() + " " + p 1. get. Height() + " " + p 1. get. Weight() + " " + p 1. get. Spouse. Name()); } } An alternative implementation is to remove throws statement from main and insert the p 1=(Person)p 2. clone(); statement in a try block with a catch block that can catch Clone. Not. Supported. Exception
Interfaces vs Abstract Classes • Both types provide a basis for the programmer to build upon – Abstract classes support single inheritance – Interfaces offer a form of multiple inheritance – Abstract classes can contain instance data and non-abstract methods – Interfaces cannot contain instance data and non-abstract methods (and can even be empty), the only variables must be constants (public static final) and the only methods must be public abstract – Abstract classes can have constructors, interfaces will not – Objects of either type can be declared but not instantiated • An interface class can itself extend other interfaces in which case implementing that interface requires implementing all of the methods of that interface and its superclasses – A naming convention: class names are usually nouns but interface names can be nouns or adjectives (particularly if they end with “able” or “ible” such as “Comparable” or “Cloneable”)
- Slides: 25