Chapter 8 Interfaces and inner classes We learnt

  • Slides: 31
Download presentation
Chapter 8: Interfaces and inner classes ● We learnt a number of mechanisms to

Chapter 8: Interfaces and inner classes ● We learnt a number of mechanisms to abstract concepts and reuse them: – – – ● ● Data abstraction (Encapsulation and Information Hiding) – making classes Reuse by Inheritance and Composition Polymorphism for making extensible programs Interfaces are another usefull mechanism in Java for better program construction Inner classes (classes inside classes) are another concept introduced in Java

Interfaces ● ● We know that public members of a class specifies the interface

Interfaces ● ● We know that public members of a class specifies the interface for that class With abstract classes and inheritance we could make interface without any specific implementation and leave the implementation for the derived classes Interface concept is a step further by specifying an interface without any specific implementation This is a powerfull tool for high quality software production: – – Write client code for Interface Then any implementation can be used at runtime

Interfaces (Cont. ) ● ● ● We can think of an Interface as a

Interfaces (Cont. ) ● ● ● We can think of an Interface as a Pure Abstract class (no method is implemented) Only method names, argument list and return types are specified without method bodies An interface can also contains fields but the fields are implicitly static and final An interface says: “This is what all classes that implement this interface will look like” So an interface makes a protocol between classes

Interface example

Interface example

Interface example (cont. ) interface Instrument { // Compile-time constant: int I = 5;

Interface example (cont. ) interface Instrument { // Compile-time constant: int I = 5; // static & final // Cannot have method definitions: void play(Note n); // Automatically public String what(); void adjust(); } class Wind implements Instrument { public void play(Note n) { System. out. println("Wind. play() " + n); } public String what() { return "Wind"; } public void adjust() {} } class Percussion implements Instrument { public void play(Note n) { System. out. println("Percussion. play() " + n); } public String what() { return "Percussion"; } public void adjust() {} } class Stringed implements Instrument { public void play(Note n) { System. out. println("Stringed. play() " + n); } public String what() { return "Stringed"; } public void adjust() {} } class Brass extends Wind { public void play(Note n) { System. out. println("Brass. play() " + n); } public void adjust() { System. out. println("Brass. adjust()"); } } class Woodwind extends Wind { public void play(Note n) { System. out. println("Woodwind. play() " + n); } public String what() { return "Woodwind"; } }

Interface example (cont. ) public class Music 5 { private static Test monitor =

Interface example (cont. ) public class Music 5 { private static Test monitor = new Test(); // Doesn't care about type, so new types // added to the system still work right: static void tune(Instrument i) { //. . . i. play(Note. MIDDLE_C); } static void tune. All(Instrument[] e) { for(int i = 0; i < e. length; i++) tune(e[i]); } public static void main(String[] args) { // Upcasting during addition to the array: Instrument[] orchestra = { new Wind(), new Percussion(), new Stringed(), new Brass(), new Woodwind() }; tune. All(orchestra); monitor. expect(new String[] { "Wind. play() Middle C", "Percussion. play() Middle C", "Stringed. play() Middle C", "Brass. play() Middle C", "Woodwind. play() Middle C" }); } }

Multiple inheritance ● ● In C++ there is multiple inheritance mechanism (i. e. inheriting

Multiple inheritance ● ● In C++ there is multiple inheritance mechanism (i. e. inheriting from several classes) But its usage is sometimes complicated and confusing – ● ● What happens if class A inherits from B and C, which both have a method with the same signature? Java designers deceided to have single inheritance to prevent such confusion But with interface mechanism we can simulate multiple imheritance

Multiple inheritance (cont. )

Multiple inheritance (cont. )

Multiple inheritance (cont. ) interface Can. Fight { void fight(); } interface Can. Swim

Multiple inheritance (cont. ) interface Can. Fight { void fight(); } interface Can. Swim { void swim(); } interface Can. Fly { void fly(); } class Action. Character { public void fight() {} } class Hero extends Action. Character implements Can. Fight, Can. Swim, Can. Fly { public void swim() {} public void fly() {} } public class Adventure { public static void t(Can. Fight x) { x. fight(); } public static void u(Can. Swim x) { x. swim(); } public static void v(Can. Fly x) { x. fly(); } public static void w(Action. Character x) { x. fight(); } public static void main(String[] args) { Hero h = new Hero(); t(h); // Treat it as a Can. Fight u(h); // Treat it as a Can. Swim v(h); // Treat it as a Can. Fly w(h); // Treat it as an Action. Character } } ///: ~

Interface or Abstract class? ● ● The first choice is interface The benefit is

Interface or Abstract class? ● ● The first choice is interface The benefit is that client code is not bound to any implementation – With abstract classes, your client code is bound to an class hierarchy – With interface any object of any class hierarchy that implements the interface is ok Use abstract classes when some common behavior should be implemented and reused As much as possible write client code to work with interfaces not any implementation (abstract or concrete classes) – This is critical for software that should be extended

Name collisions with interfaces interface I 1 { void f(); } interface I 2

Name collisions with interfaces interface I 1 { void f(); } interface I 2 { int f(int i); } interface I 3 { int f(); } class C { public int f() { return 1; } } class C 2 implements I 1, I 2 { public void f() {} public int f(int i) { return 1; } // overloaded } class C 3 extends C implements I 2 { public int f(int i) { return 1; } // overloaded } class C 4 extends C implements I 3 { // Identical, no problem: public int f() { return 1; } } // Methods differ only by return type: //! class C 5 extends C implements I 1 {} //! interface I 4 extends I 1, I 3 {} ///: ~ Interface. Collision. java: 23: f( ) in C cannot implement f( ) in I 1; attempting to use incompatible return type found : int required: void Interface. Collision. java: 24: interfaces I 3 and I 1 are incompatible; both define f( ), but with different return type

Extending an interface Monster { void menace(); } interface Dangerous. Monster extends Monster {

Extending an interface Monster { void menace(); } interface Dangerous. Monster extends Monster { void destroy(); } interface Lethal { void kill(); } class Dragon. Zilla implements Dangerous. Monster { public void menace() {} public void destroy() {} } interface Vampire extends Dangerous. Monster, Lethal { void drink. Blood(); } class Very. Bad. Vampire implements Vampire { public void menace() {} public void destroy() {} public void kill() {} public void drink. Blood() {} } public class Horror. Show { static void u(Monster b) { b. menace(); } static void v(Dangerous. Monster d) { d. menace(); d. destroy(); } static void w(Lethal l) { l. kill(); } public static void main(String[] args) { Dangerous. Monster barney = new Dragon. Zill u(barney); v(barney); Vampire vlad = new Very. Bad. Vampire(); u(vlad); v(vlad); w(vlad); } } ///: ~

Grouping constants in an interface ● ● Since any fields we put in an

Grouping constants in an interface ● ● Since any fields we put in an interface is static and final, interface is a convenient tool to create group of constants This way it is like enum in C/C++ public interface Months { int JANUARY = 1, FEBRUARY = 2, MARCH = 3, APRIL = 4, MAY = 5, JUNE = 6, JULY = 7, AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10, NOVEMBER = 11, DECEMBER = 12; } ///: ~

Initializing fields in interfaces public class Test. Rand. Vals { public interface Rand. Vals

Initializing fields in interfaces public class Test. Rand. Vals { public interface Rand. Vals { private static Test monitor = new Test(); Random rand = new Random(); public static void main(String[] args) { int random. Int = rand. next. Int(10); System. out. println(Rand. Vals. random. Int); long random. Long = rand. next. Long() * 10; System. out. println(Rand. Vals. random. Long); float random. Float = rand. next. Long() * 10; double random. Double = rand. next. Double() * 10; System. out. println(Rand. Vals. random. Float); System. out. println(Rand. Vals. random. Double); } monitor. expect(new String[] { "%% -? \d+", "%% -? \d\. \d+E? -? \d+" }); } } ///: ~

Nesting interfaces class A { interface B { void f(); } public class BImp

Nesting interfaces class A { interface B { void f(); } public class BImp implements B { public void f() {} } private class BImp 2 implements B { public void f() {} } public interface C { void f(); } class CImp implements C { public void f() {} } private class CImp 2 implements C { public void f() {} } private interface D { void f(); } private class DImp implements D { public void f() {} } public class DImp 2 implements D { public void f() {} } public D get. D() { return new DImp 2(); } private D d. Ref; public void receive. D(D d) { d. Ref = d; d. Ref. f(); } } interface E { interface G { void f(); } // Redundant "public": public interface H { void f(); } void g(); // Cannot be private within an interface: //! private interface I {} }

Nesting interfaces (Cont. ) public class Nesting. Interfaces { public class BImp implements A.

Nesting interfaces (Cont. ) public class Nesting. Interfaces { public class BImp implements A. B { public void f() {} } class CImp implements A. C { public void f() {} } // Cannot implement a private interface except // within that interface's defining class: //! class DImp implements A. D { //! public void f() {} //! } class EImp implements E { public void g() {} } class EGImp implements E. G { public void f() {} } class EImp 2 implements E { public void g() {} class EG implements E. G { public void f() {} } } public static void main(String[] args) { A a = new A(); // Can't access A. D: //! A. D ad = a. get. D(); // Doesn't return anything but A. D: //! A. DImp 2 di 2 = a. get. D(); // Cannot access a member of the interface: //! a. get. D(). f(); // Only another A can do anything with get. D(): A a 2 = new A(); a 2. receive. D(a. get. D()); } } ///: ~

Inner classes ● ● ● We can put definitions of classes inside other classes.

Inner classes ● ● ● We can put definitions of classes inside other classes. Those classes are named as inner classes With inner classes we can group related class definitions and control access to them Inner classes concept is different from composition

Making inner classes public class Parcel 1 { class Contents { private int i

Making inner classes public class Parcel 1 { class Contents { private int i = 11; public int value() { return i; } } class Destination { private String label; Destination(String where. To) { label = where. To; } String read. Label() { return label; } } // Using inner classes looks just like // using any other class, within Parcel 1: public void ship(String dest) { Contents c = new Contents(); Destination d = new Destination(dest); System. out. println(d. read. Label()); } public static void main(String[] args) { Parcel 1 p = new Parcel 1(); p. ship("Tanzania"); } } ///: ~

Returing objects of inner class type public class Parcel 2 { class Contents {

Returing objects of inner class type public class Parcel 2 { class Contents { private int i = 11; public int value() { return i; } } class Destination { private String label; Destination(String where. To) { label = where. To; } String read. Label() { return label; } } public Destination to(String s) { return new Destination(s); } public Contents cont() { return new Contents(); } public void ship(String dest) { Contents c = cont(); Destination d = to(dest); System. out. println(d. read. Label()); } public static void main(String[] args) { Parcel 2 p = new Parcel 2(); p. ship("Tanzania"); Parcel 2 q = new Parcel 2(); // Defining references to inner classes: Parcel 2. Contents c = q. cont(); Parcel 2. Destination d = q. to("Borneo"); } } ///: ~

Inner classes and upcasting ● ● What is the usage for inner classes. If

Inner classes and upcasting ● ● What is the usage for inner classes. If it is for hidding, we can have same effects with package access class definition One usage is with upcasting This way you can completely hide the implementation

Inner classes and upcasting (cont. ) //: c 08: Destination. java public interface Destination

Inner classes and upcasting (cont. ) //: c 08: Destination. java public interface Destination { String read. Label(); } ///: ~ //: c 08: Contents. java public interface Contents { int value(); } ///: ~ public class Test. Parcel { public static void main(String[] args) { Parcel 3 p = new Parcel 3(); Contents c = p. cont(); Destination d = p. dest("Tanzania"); // Illegal -- can't access private class: //! Parcel 3. PContents pc = p. new PContents(); } } ///: ~ class Parcel 3 { private class PContents implements Contents { private int i = 11; public int value() { return i; } } protected class PDestination implements Destination { private String label; private PDestination(String where. To) { label = where. To; } public String read. Label() { return label; } } public Destination dest(String s) { return new PDestination(s); } public Contents cont() { return new PContents(); } }

Inner classes in methods and scops ● ● We can define inner classes inside

Inner classes in methods and scops ● ● We can define inner classes inside a method or a scop! Why we need that? – – You want to create a refrence that implements an interface. The implementation is only used inside the method or scope and there is no need that other parts of code (the owner class or others know about that implementation)

Inner classes in methods and scops(cont. ) ● For example we can do following:

Inner classes in methods and scops(cont. ) ● For example we can do following: – – – Define a class inside a method Define a class within a scope inside a class Define an anonymous class implementing an interface Define an anonymous class that performs field initialization Define an anonymous class that performs constructuion using instance initialization

Definining Inner classes in methods public class Parcel 4 { public Destination dest(String s)

Definining Inner classes in methods public class Parcel 4 { public Destination dest(String s) { class PDestination implements Destination { private String label; private PDestination(String where. To) { label = where. To; } public String read. Label() { return label; } } return new PDestination(s); } public static void main(String[] args) { Parcel 4 p = new Parcel 4(); Destination d = p. dest("Tanzania"); } } ///: ~

Nesting inner class in a scope public class Parcel 5 { private void internal.

Nesting inner class in a scope public class Parcel 5 { private void internal. Tracking(boolean b) { if(b) { class Tracking. Slip { private String id; Tracking. Slip(String s) { id = s; } String get. Slip() { return id; } } Tracking. Slip ts = new Tracking. Slip("slip"); String s = ts. get. Slip(); } // Can't use it here! Out of scope: //! Tracking. Slip ts = new Tracking. Slip("x"); } public void track() { internal. Tracking(true); } public static void main(String[] args) { Parcel 5 p = new Parcel 5(); p. track(); } } ///: ~

Annonymous inner classes public class Parcel 6 { public Contents cont() { return new

Annonymous inner classes public class Parcel 6 { public Contents cont() { return new Contents() { private int i = 11; public int value() { return i; } }; // Semicolon required in this case } public static void main(String[] args) { Parcel 6 p = new Parcel 6(); Contents c = p. cont(); } } ///: ~ This is a shorthand for: class My. Contents implements Contents { private int i = 11; public int value() { return i; } } return new My. Contents();

Annonymous inner classes with arguments for base class constructor public class Wrapping { private

Annonymous inner classes with arguments for base class constructor public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } ///: ~ public class Parcel 7 { public Wrapping wrap(int x) { // Base constructor call: return new Wrapping(x) { // Pass constructor argum public int value() { return super. value() * 47; } }; // Semicolon required } public static void main(String[] args) { Parcel 7 p = new Parcel 7(); Wrapping w = p. wrap(10); } } ///: ~

Field initialization in anonymous class public class Parcel 8 { // Argument must be

Field initialization in anonymous class public class Parcel 8 { // Argument must be final to use inside // anonymous inner class: public Destination dest(final String dest) { return new Destination() { private String label = dest; public String read. Label() { return label; } }; } public static void main(String[] args) { Parcel 8 p = new Parcel 8(); Destination d = p. dest("Tanzania"); } } ///: ~

Constructor for anonymous inner classes? ● ● ● public class Anonymous. Constructor { private

Constructor for anonymous inner classes? ● ● ● public class Anonymous. Constructor { private static Test monitor = new Test(); public static Base get. Base(int i) { return new Base(i) { { System. out. println("Inside instance initializer" } public void f() { System. out. println("In anonymous f()"); } }; } public static void main(String[] args) { Base base = get. Base(47); base. f(); monitor. expect(new String[] { "Base constructor, i = 47", "Inside instance initializer", "In anonymous f()" }); } } ///: ~ Since anonymous inner classes has no name, we can't define constructors for them What to do when we need some initializations? We can do initialization in a block inside the class abstract class Base { public Base(int i) { System. out. println("Base constructor, i = " + i); } public abstract void f(); }

Instance initialization for parcel class Parcel 9 { example public private static Test monitor

Instance initialization for parcel class Parcel 9 { example public private static Test monitor = new Test(); public Destination dest(final String dest, final float price) { return new Destination() { private int cost; // Instance initialization for each object: { cost = Math. round(price); if(cost > 100) System. out. println("Over budget!"); } private String label = dest; public String read. Label() { return label; } }; } public static void main(String[] args) { Parcel 9 p = new Parcel 9(); Destination d = p. dest("Tanzania", 101. 395 F); monitor. expect(new String[] { "Over budget!" }); } } ///: ~

Link to the outer class public Selector get. Selector() { interface Selector { boolean

Link to the outer class public Selector get. Selector() { interface Selector { boolean end(); Object current(); void next(); } public class Sequence { private static Test monitor = new Test(); private Object[] objects; private int next = 0; public Sequence(int size) { objects = new Object[size]; } public void add(Object x) { if(next < objects. length) objects[next++] = x; } private class SSelector implements Selector { private int i = 0; public boolean end() { return i == objects. length; } public Object current() { return objects[i]; } public void next() { if(i < objects. length) i++; } } return new SSelector(); } public static void main(String[] args) { Sequence sequence = new Sequence(10); for(int i = 0; i < 10; i++) sequence. add(Integer. to. String(i)); Selector selector = sequence. get. Selector(); while(!selector. end()) { System. out. println(selector. current()); selector. next(); } monitor. expect(new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }); } } ///: ~