- Java provides a mechanism called abstract method, which is incomplete; Declaration only, no method body. Classes that contain abstract methods are called abstract classes. If a class contains one or more abstract methods, the class must be qualified as abstract. If you inherit from an abstract class and want to create an object of the exported class, you must provide method definitions for all abstract methods in the base class. If we don't, the exported class is also an abstract class, and the compiler will force us to qualify the class with the keyword abstract. Abstract classes cannot create objects.
- Abstract keyword allows people to create one or more undefined methods in a class - providing an interface part, but not any corresponding concrete implementation, which is created by the successor of this class. The interface keyword produces a completely abstract class that does not provide any implementation at all. Interfaces can also contain domains, but these domains are implicitly static and final. When implementing an interface, the methods defined in the interface must be defined as public.
- As in the following example, creating a method that can have different behaviors according to different parameter objects passed is called policy design pattern. Such methods include the fixed part of the algorithm to be executed, while the "policy" includes the changing part. A policy is a parameter object passed in, which contains the code to be executed. In this example, the Processor object is a policy. In main(), you can see that three different types of policies are applied to the s object of String type.
package thinkinginjava.charpenter9; import java.util.Arrays; /** * @author Spring-_-Bear * @version 2021/10/2 10:11 */ public class Apply { public static void process(Processor p, Object o) { System.out.println("Using Processor " + p.name()); System.out.println(p.process(o)); } public static String s = "Disagreement with beliefs is by definition incorrect"; public static void main(String[] args) { process(new Upcase(), s); process(new Downcase(), s); process(new Splitter(), s); } } class Processor { public String name() { return getClass().getSimpleName(); } Object process(Object input) { return input; } } class Upcase extends Processor { @Override String process(Object input) { return ((String) input).toUpperCase(); } } class Downcase extends Processor { @Override String process(Object input) { return ((String) input).toLowerCase(); } } class Splitter extends Processor { @Override String process(Object input) { /** * The split() argument divides a String into pieces: */ return Arrays.toString(((String) input).split(" ")); } }
- Adapter design pattern: the adapter code will accept the interface you have and generate the interface you need. Decouple the interface from the specific implementation, so that the interface can be applied to a variety of different specific implementations, so the code is more reusable.
- Multiple inheritance in Java: if you want to inherit from a non interface class, you can only inherit from one class. The rest of the base elements must be interfaces. All interface names need to be placed after the implements keyword, separated by commas.
- The core reason for using interfaces: to be able to transition up to multiple base types (and the resulting flexibility). However, the second reason for using interfaces is indeed the same as using abstract classes: prevent client programmers from creating objects of this class and ensure that this is only an interface.
- Extending interfaces through inheritance classes: through inheritance, you can easily add new method declarations to interfaces, and you can also combine multiple interfaces in new interfaces through inheritance.
package thinkinginjava.charpenter9; /** * @author Spring-_-Bear * @version 2021/10/3 10:12 */ public class Adventure { public static void t(CanFight canFight){ canFight.fight(); } public static void u(CanSwim canSwim) { canSwim.swim(); } public static void v(CanFly canFly){ canFly.fly(); } public static void w(ActionCharacter actionCharacter){ actionCharacter.fight(); } public static void main(String[] args) { Hero hero = new Hero(); t(hero); u(hero); v(hero); w(hero); } } interface CanFight { void fight(); } interface CanSwim { void swim(); } interface CanFly { void fly(); } class ActionCharacter { public void fight() { } } class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { @Override public void swim() { } @Override public void fight() { super.fight(); } @Override public void fly() { } }
- Any field in the interface is automatically static and final, so the interface becomes a very convenient tool for creating constant groups. The field defined in the interface cannot be "empty final", but it can be initialized by a non constant expression. Since domains are static, they can be initialized when the class is first loaded. Of course, these fields are not part of the interface, and their values are stored in the static storage area of the interface.
- Interfaces can be nested within classes or other interfaces. When implementing an interface, you do not need to implement any interface nested within it.
10. Interface and factory: interface is the way to realize multiple inheritance, and the typical way to generate an object is the factory method design pattern. This is different from calling the constructor directly. What we call in the factory object is the creation method, and the factory object will generate the object of an implementation of the interface. Theoretically, in this way, our code will be completely separated from the implementation of the interface.
package thinkinginjava.charpenter9; /** * @author Spring-_-Bear * @version 2021/10/3 11:26 */ public class Factories { public static void serviceConsumer(ServiceFactory fact){ Service service = fact.getService(); service.method1(); service.method2(); } public static void main(String[] args) { serviceConsumer(new Implementation1Factory()); serviceConsumer(new Implementation2Factory()); } } interface Service{ void method1(); void method2(); } interface ServiceFactory{ Service getService(); } class Implementation1 implements Service{ Implementation1() { } @Override public void method1() { System.out.println("Implementation1.method1()"); } @Override public void method2() { System.out.println("Implementation2.method2()"); } } class Implementation1Factory implements ServiceFactory{ @Override public Service getService() { return new Implementation1(); } } class Implementation2 implements Service{ Implementation2(){} @Override public void method1() { System.out.println("Implementation2.method1()"); } @Override public void method2() { System.out.println("Implementation2.method2()"); } } class Implementation2Factory implements ServiceFactory{ @Override public Service getService() { return new Implementation2(); } }
Why do we want to add this extra level of indirection? A common reason is to create a framework.
Exercise 1: modify Rodent in exercise 9 of Chapter 8 to make it an abstract class. Whenever possible, declare Rodent's methods as abstract methods.
Exercise 2: create an abstract class that does not contain any abstract methods and verify that we cannot create any instances of the class.
Exercise 3: create a base class that contains the abstract method print() and override it in the exported class. The overwritten method version can print the value of an integer variable defined in the exported class. Where the variable is defined, give it a non-zero value. Call this method in the constructor of the base class. Now, in the main() method, create an export class object and call its print method. Please explain what happened.
package thinkinginjava.charpenter9; /** * @author Spring-_-Bear * @version 2021/10/3 11:47 */ public class HpPrinter extends Printer { private int temp = 88; @Override void print(){ System.out.println("The values of temp is " + temp); } public static void main(String[] args) { HpPrinter printer = new HpPrinter(); printer.print(); } } abstract class Printer { Printer() { print(); } abstract void print(); }.
Exercise 4: create an abstract class that does not contain any methods, export a class from it, and add a method. Create a static method that can accept a reference to the base class, transform it down to the exported class, and then call the static method. In main(), show its operation. Then, add an abstract declaration to the methods in the base class, so that there is no need for downward transformation.
package thinkinginjava.charpenter9; /** * @author Spring-_-Bear * @version 2021/10/3 12:38 */ public class Dog extends Animal { void eat() { System.out.println("Dog.eat()"); } static void method(Animal animal) { ((Dog) animal).eat(); } public static void main(String[] args) { Animal dog = new Dog(); Dog.method(dog); } } abstract class Animal { }
Exercise 5: create an interface with three methods in one package, and then implement the interface in another package.
Exercise 6: prove that all methods in the interface are automatically public.
Exercise 7: revise exercise 9 in Chapter 8 to make Rodent an interface.
Exercise 8: in polymer.Sandwich.java, create the interface FastFood and add appropriate methods, and then modify Sandwich to implement the FastFood interface.
Exercise 9: refactor Music5.java to move public methods in Wind, discussion, and Stringed into an abstraction.
Exercise 10: modify Music5.java and add Playable interface. Move the declaration of play() from Instrument to Playable. Add Playable to the export class by including it in the Implements list. Modify tune() to accept Playable instead of Instrument as a parameter.
Exercise 11: create a class that has a method to accept a parameter of type String. The result is to interchange each pair of characters in the parameter. Adapt this class so that it can be used in interfaceprocessor.Apply.process().
Exercise 12: in Adventure.java, add a CanClimb interface according to the style of other interfaces.
Exercise 13: create an interface, inherit two interfaces from that interface, and then multiply inherit the second interface from the latter two interfaces.
Exercise 14: create three interfaces, each containing two methods. Inherits an interface that combines the three interfaces and adds a new method. Create a class that implements the new interface and inherits a concrete class. Now write four methods, each of which accepts one of these four interfaces as a parameter. In the main() method, create the object of this class and pass it to the four methods.
package thinkinginjava.charpenter9; /** * @author Spring-_-Bear * @version 2021/10/3 15:25 */ public class Person extends Animal implements Behavior { @Override public void forward() { System.out.println("Person.forward()"); } @Override public void back() { System.out.println("Person.back()"); } @Override public void chew() { System.out.println("Person.chew()"); } @Override public void gulping() { System.out.println("Person.gulping()"); } @Override public void loud() { System.out.println("Person.loud()"); } @Override public void low() { System.out.println("Person.low()"); } @Override public void drink() { System.out.println("Person.drink()"); } @Override void smell() { System.out.println("Person.smell()"); } public static void move(Move move) { move.back(); move.forward(); } public static void eat(Eat eat) { eat.chew(); eat.gulping(); } public static void speak(Speak speak) { speak.loud(); speak.low(); } public static void behavior(Behavior behavior) { behavior.drink(); } public static void main(String[] args) { Person person = new Person(); move(person); eat(person); speak(person); behavior(person); } } interface Move { void forward(); void back(); } interface Eat { void chew(); void gulping(); } interface Speak { void loud(); void low(); } interface Behavior extends Move, Eat, Speak { void drink(); } class Animal { Animal() { System.out.println("Animal constructor"); } void smell() { System.out.println("Animal.smell()"); } }
Exercise 15: change the previous exercise to: create an abstract class and inherit it to an exported class.
Exercise 16: create a class that will generate a char sequence and adapt the class so that it can become an input to the Scanner object.
Exercise 17: prove that the fields in the interface are implicitly static and final.
Exercise 18: create a Cycle interface and its Unicycle, Bicycle, and Tricycle implementations. Create corresponding factories for each type of Cycle, and then write code to use these factories.
package thinkinginjava.charpenter9; /** * @author Spring-_-Bear * @version 2021/10/3 15:45 */ public class FactoryMode { public static void cycleConsumer(CycleFactory cycleFactory) { Cycle cycle = cycleFactory.getCycle(); cycle.back(); cycle.forward(); } public static void main(String[] args) { cycleConsumer(new BicycleFactory()); cycleConsumer(new UnicycleFactory()); cycleConsumer(new TricycleFactory()); } } interface Cycle { void back(); void forward(); } interface CycleFactory { Cycle getCycle(); } class Unicycle implements Cycle { @Override public void back() { System.out.println("Unicycle.back()"); } @Override public void forward() { System.out.println("Unicycle.forward()"); } } class Bicycle implements Cycle { @Override public void back() { System.out.println("Bicycle.back()"); } @Override public void forward() { System.out.println("Bicycle.back()"); } } class Tricycle implements Cycle { @Override public void back() { System.out.println("Tricycle.back()"); } @Override public void forward() { System.out.println("Tricycle.forward()"); } } class UnicycleFactory implements CycleFactory { @Override public Cycle getCycle() { return new Unicycle(); } } class BicycleFactory implements CycleFactory { @Override public Cycle getCycle() { return new Bicycle(); } } class TricycleFactory implements CycleFactory { @Override public Cycle getCycle() { return new Tricycle(); } }
Exercise 19: use the factory method to create a frame that can perform coin tossing and dice rolling functions.
package thinkinginjava.charpenter9; import java.util.Random; /** * @author Spring-_-Bear * @version 2021/10/3 16:02 */ public class PlayGames { public static void startGame(GameFactory gameFactory) { Game game = gameFactory.getGame(); game.play(); } public static void main(String[] args) { startGame(new CoinTossFactory()); startGame(new CrapsGameFactory()); } } interface Game { void play(); } interface GameFactory { Game getGame(); } class CoinToss implements Game { @Override public void play() { Random random = new Random(); for (int i = 0; i < 5; i++) { int num = random.nextInt(2); if (num == 1) { System.out.println("The first" + (i + 1) + "The coin toss turned positive"); } else { System.out.println("The first" + (i + 1) + "The result of a coin flip is negative"); } } } } class CrapsGame implements Game { @Override public void play() { Random random = new Random(); for (int i = 0; i < 6; i++) { int num = random.nextInt(6) + 1; System.out.println("The first" + (i + 1) + "The result of one roll of dice is" + num); } } } class CoinTossFactory implements GameFactory { @Override public Game getGame() { return new CoinToss(); } } class CrapsGameFactory implements GameFactory { @Override public Game getGame() { return new CrapsGame(); } }