410 likes | 428 Views
Discover what interfaces are in Java programming, how they enforce functionality in classes, and why they are crucial for maintaining consistency in object-oriented design. Learn through code examples and explanations.
E N D
What is an Interface?? An interface is a ‘class’ which contains ONLYabstract methods. public interface Capitalizable { public abstract String outCaps() ; //returns String representation of object in all capital letters public abstract String outFirstCap(); //returns String representation of object w/ first char in caps public abstract String outNoCaps(); // returns String representation of object in all lowercase }
What is an Interface FOR?? An interface can be used to FORCE a class to provide certain functionality. Suppose we needed to be certain that the class Phrase provided a way to output data: • in all capitals • with first letter capitalized • in all lower case
public class Phrase implements Capitalizable { String the_data; public Phrase() { // code for this method } public void addWord(String word) { // code for this method } public int howMany() { // code for this method } //Phrase MUST fully define all methods which are in Capitalizable
//the class will NOT compile without these methods!! public String outCaps() { // code for this method } public String outFirstCap(){ // code for this method } public String outNoCaps() { // code for this method } ADDITIONALLY, an object of class Phrase is ALSO an object of type Capitalizable!!!!!!!!!!!
Why might I need to ensure that a class offers the methods listed in Capitalizable?? Suppose we wanted to write a program which creates Sentence objects, Paragraph objects, Word object, and BlockText objects And our program was to store these objects and output them all in capital letters.
Sentence [] sobj; Paragraph [] pobj; Word [] wobj; Blocktext [] bobj; sobj = new Sentence [100]; pobj = new Paragraph [100]; wobj = new Word [100]; bobj = new Blocktext [100]; sobj[0] = new Sentence(); pobj[0] = new Paragraph(); wobj[0] = new Word(); bobj[0] = new BlockText(); ….. // other assignments sobj[15] = new Sentence(); pobj[15] = new Paragraph(); wobj[15] = new Word(); bobj[15] = new BlockText();
for (int index=0; index < 15; index++) System.out.println( “Object at index “ + index + “ is “ + sobj[index].outCaps() ); for (int index=0; index < 15; index++) System.out.println( “Object at index “ + index + “ is “ + pobj[index].outCaps() ); for (int index=0; index < 15; index++) System.out.println( “Object at index “ + index + “ is “ + wobj[index].outCaps() ); for (int index=0; index < 15; index++) System.out.println( “Object at index “ + index + “ is “ + pobj[index].outCaps() ); } There will be a problem if any of the objects in the array does not provide a method called outCaps, with exactly that signature…
Rather than leaving this up to the individual programmers of the Sentence, Paragraph, Word and BlockText class, it would be nice if the COMPILER could force these classes to implement the methods we need…… We can, if the application program code insists that those classes must IMPLEMENT the Capitalizable interface…………..
Capitalizable [] sobj; Capitalizable [] pobj; Capitalizable [] wobj; Capitalizable [] bobj; sobj = new Capitalizable [100]; pobj = new Capitalizable [100]; wobj = new Capitalizable [100]; bobj = new Capitalizable [100]; sobj[0] = new Sentence(); // this is fine, these objects are Capitalizable pobj[0] = new Paragraph(); wobj[0] = new Word(); bobj[0] = new BlockText(); ….. // other assignments sobj[15] = new Sentence(); pobj[15] = new Paragraph(); wobj[15] = new Word(); bobj[15] = new BlockText();
public class Sentence implements Capitalizable{ // this class will ONLY not compile if it does not contain the methods specified in the Capitalizable interface } public class Paragraph implements Capitalizable{ // this class will ONLY not compile if it does not contain the methods specified in the Capitalizable interface } public class BlockText implements Capitalizable{ // this class will ONLY not compile if it does not contain the methods specified in the Capitalizable interface }
// one array can store varying types of objects // all objects can be output in capitals using the same code – // because it is KNOWN that each object has an outCaps method Capitalizable [] textobj; textobj = new Capitalizable [60]; textobj[0] = new Sentence(); textobj[1] = new Paragraph(); textobj[2] = new Word(); ….. // other assignments textobj[59] = new BlockText(); for (int index=0; index < 60; index++) System.out.println( “Object at index “ + index + “ is “ + textobj[index].outCaps() );
for (int index=0; index < 60; index++) System.out.println( “Object at index “ + index + “ is “ + textobj[index].outCaps() ); //different methods are being executed depending the type of each array element How does JAVA know which method outCaps() is being output at any one time??
The Java language provides ‘polymorphism’, or late binding. A polymorphic language waits until RUN TIME to bind a method call to the actual code that will be run. (this allows Java to KNOW the type of object that is making the call) (An object oriented language provides: encapsulation of data and methods via classes polymorphism inheritance )
You have already seen an interface…. Shape * The Shape interface provides a number of abstract methods, just like any other interface. * Rectangle2D, Line2D, Ellipse2D, RoundRectangle2D, Rectangle2D all realize (implement) the Shape interface. * So a object created from any of these classes is an object of type Shape. * An array declared and instantiated as: Shape [ ] shapelist = new Shape[40]; is capable of storing 40 objects instantiated from any of these classes.
For example: import ……. public class MyShapes extends Applet{ private Shape [ ] shapelist = new Shape[4]; public MyShapes() { //create the draw shapelist[0] = new Rectangle2D.Double(x,y,width,height); shapelist[1] = new Ellipse2D.Double(x,y,width,height); shapelist[2] = new Line2D.Double(x,y,width,height); shapelist[3] = new RoundRectangle2D.Double(x,y,width,height); }
MyShapes class continued…. public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; // draw all the shapes for (int index = 0; index < 4; index ++ ) g2.draw(shapelist[index]); }
The MyShapes class does not make use of the fact that all the objects in the array have many method names in common. This applet only uses their common type to allow one array to be used to store all the shapes. HOWEVER, the draw(Shape) method of the Graphics2D class probably does call upon methods of the passed objects. for example: Any object of type Shape has a method called getBoundary() . This method would be quite useful in drawing any shape … and Is probably called within the draw method code..
One method which was in the Array class is the the Arrays class provided: static void sort (Object [] array) static int binarySearch(Object[] array, Object key) This means: Dog[] values = new Dog[55]; Dog adog; …. code which initializes the array Arrays.sort(values); ….. int position = Arrays.binarySearch(values,aDog);
How does sort know how to order an array of Dogs?? How can binarySearch determine if 2 Dog objects are equal?? In order to use these methods with a class type, that class type must define it’s ordering ……… That class type must implement the Comparable interface)
public interface Comparable { public abstract int compareTo(Object obj); } Usage: // This method returns 0 if implicit object (this) is equal to obj // This method returns a negative value if implicit object is less than obj // This method returns a positive value if obj is larger than implicit object
Suppose we wanted to write an applet that would read Dog data from a file, store them in an array, sort them and draw them in order. Our Dog class would have to implement the Comparable interface.. Let’s code that………
another use of interfaces … Suppose we had a class named BankAccounts, where an object of this class represented one bank account. (one method of this class is getBalance, which gives the account balance). Now we want to create a class named BADataSet, which can monitor a group BankAccount objects, keeping track of the total funds and largest account.
BADataSet might look like this: public class BADataSet { private double sum; //total of all account balances private BankAccount maximum; //account w/ highest balance private int count; //number of accounts in set public void add(BankAccount x){ sum = sum + x.getBalance(); if (count == 0 || maximum.getBalance()< x.getBalance()) maximum = x; count++;} public BankAccount getMaximum(){ return maximum;}
Suppose we had a class named Coin, where an object of this class represents one coin. (one method of this class is getTotal, which gives the value of the coins). Now we want to create a class named CDataSet, which can keeps track of a number of Coin objects, keeping track of the total cash and largest denomination of coin.
CDataSet might look like this: public class CDataSet { private double sum; //total of all coin objects private Coin maximum; //coin object with highest value private int count; //number of coin objects in set public void add(Coin x){ sum = sum + x.getTotal(); if (count == 0 || maximum.getTotal()< x.getTotal()) maximum = x; count++;} public Coin getMaximum(){ return maximum;}
Gee, both of those classes look quite alike. In fact, except for: * getBalance() and getTotal() * parameter/return types it is the same code. How can we write ONE DataSet class which can handle both types of objects??
Suppose both classes could agree on the same method name, say, getMeasure • DataSet could call that method: sum = sum + x.getMeasure();if (count == 0 || maximum.getMeasure() < x.getMeasure()) maximum = x; • Define an interface: public interface Measurable { public abstract double getMeasure(); }
Remember …. • when a class ‘implements’ an interface it must provide a full implementation of the abstract methods in the interface • So, if both BankAccount and Coin classes ‘implemented’ the measurable interface, we know they have the method getMeasure
class BankAccount implements Measurable{ double balance; //additional variables and methods public double getMeasure(){ return balance;} } class Coin implements Measurable{ double value; //additional variables and methods public double getMeasure(){ return value;} }
public class DataSet { private double sum;private ********* maximum;private int count; public void add(********* x){ sum = sum + x.getMeasure(); if (count == 0 || maximum.getMeasure() < x.getMeasure()) maximum = x; count++;} public ************ getMaximum(){ return maximum;} WE have taken care of the method name, but what data type will work, can’t be BankAccount and can’t be Coin!! }
Remember …. • when a class ‘implements’ an interface, objects of that class are object of the interface type (in addition to also being objects of the specific class) • So, if both BankAccount and Coin classes ‘implemented’ the Measurable interface, we know they are both objects of class Measurable !
public class DataSet { private double sum;private Measurable maximum;private int count; public void add(Measureable x){ sum = sum + x.getMeasure(); if (count == 0 || maximum.getMeasure() < x.getMeasure()) maximum = x; count++;} public Measurable getMaximum(){ return maximum;} }
// This program tests the DataSet class. public class DataSetTest{ public static void main(String[] args) { DataSet bankData = new DataSet(); bankData.add(new BankAccount(0)); bankData.add(new BankAccount(10000)); bankData.add(new BankAccount(2000)); System.out.println("Average balance = " + bankData.getAverage()); Measurable max = bankData.getMaximum(); System.out.println("Highest balance = " + max.getMeasure());
DataSet coinData = new DataSet(); coinData.add(new Coin(0.25, "quarter")); coinData.add(new Coin(0.1, "dime")); coinData.add(new Coin(0.05, "nickel")); System.out.println("Average coin value = " + coinData.getAverage()); max = coinData.getMaximum(); System.out.println("Highest coin value = " + max.getMeasure()); } }
UML Diagram Note that DataSet is decoupled from BankAccount, Coin
Converting Between Types • Can convert from class type to realized interface type:BankAccount account = new BankAccount(10000);Measurable x = account; // OK • Same interface type variable can hold reference to Coinx = new Coin(0.1, "dime"); // OK • Cannot convert between unrelated typesx = new Rectangle(5, 10, 20, 30); // ERROR
Casts • Add coin objects toDataSetDataSet coinData = new DataSet();coinData.add(new Coin(0.25, "quarter"));coinData.add(new Coin(0.1, "dime"));... • Get largest coin withgetMaximummethod:Measurable max = coinData.getMaximum(); If the coin class also has a method called ‘getName’ which returns the type of coin as a String…… • You know it's a coin, but the compiler doesn't. Apply a cast:Coin maxCoin = (Coin)max;String name = maxCoin.getName(); • If you are wrong and max isn't a coin, the compiler throws an exception
The instanceof Operator • Use instanceof for safe casts: if (max instanceof Coin){ Coin maxCoin = (Coin)max; . . .}
object instanceof ClassName Example: if (x instanceof Coin){ Coin c = (Coin)x;} Purpose: To return true if the object is an instance of ClassName (or one of its subclasses), false otherwise
Polymorphism • Interface variable x holds reference to object of a class that realizes (implements) the interfaceMeasurable x;x = new BankAccount(10000);x = new Coin(0.1, "dime"); • You can call any of the interface methods:double m = x.getMeasure(); • Which method is called?
Which method is called? Depends on the actual object. If x refers to a bank account, calls BankAccount.getMeasure If x refers to a coin, calls Coin.getMeasure Java can handle this because it supports polymorphism (greek: many shapes) => one reference can refer to different types of objects In the case of a polymorphic object, the actual method which will be called is determined by the type of the object. How does this work?? The JVM uses the actual type of object to determine method to execute at runtime !! (dynamic or late binding) This is called polymorphism. *Different from overloading. Overloading is resolved by the compiler (at compile time).