Thinking in Java is all dry stuff
I. abstract classes and abstract methods
Abstract Method: This method only has declaration but no method body. Here is the grammar of abstract method life.
abstract void f();
Classes containing abstract methods are called abstract classes. If a class contains one or more abstract methods, the class must be limited to abstract, and the compiler will not allow the creation of an abstract class object directly. The correct way is to inherit from the abstract class and define the abstract method, so that the object of the class can be created. But if the derived class has abstract methods, then the class should also be declared abstract as an abstract class. Simple uses are as follows
package tij.interfacedemo; public class Test { public static void main(String[] args) { new Wind().play(Note.MIDDLE_A); } } enum Note{ MIDDLE_A,MIDDLE_B,MIDDLE_C; } abstract class Instrument{//Abstract parent class private int i;// public abstract void play(Note n); public String what(){ return "Instrument"; } public abstract void adjust(); } class Wind extends Instrument{ public void play(Note n){ System.out.println("Wind.play() "+n); } public String what(){ return "wind"; } public void adjust(){ } }
In fact, it can be found that there is no difference from ordinary inheritance.
II. Interface
Interface is a completely abstract class without any specific implementation method. It allows the creator to create method names, parameter lists, and return types of methods, but without any method body. The idea of an interface is, "The classes that implement this interface all look like this." And the interface has a series of inherited characteristics, such as upward transformation and so on.
The interface can add public keywords (if you want to add public, you can't add private and protected), and if you don't add it, you will have default access rights (package access rights). The method in the interface is automatically public abstract. Interfaces can also contain domains (that is, reference variables or basic variables) and are automatically static final and cannot be modified by private and protected. The following examples are given:
package tij.interfacedemo; public class Test { public static void main(String[] args) { new Wind().play(Note.MIDDLE_A); } } enum Note{ MIDDLE_A,MIDDLE_B,MIDDLE_C; } interface Instrument{// int i=5;// void play(Note n); 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(){ } }
Complete decoupling
This is a good function of the interface.
In inheritance, if a method accepts an instance of a class as a parameter, then you can use an instance of the class or subclass as an input parameter, as follows:
package tij.interfacedemo; import java.util.Arrays; public class Test { static void process(Processor p, Object s) { System.out.println("Using Processor:" + p.name()); System.out.println(p.process(s)); } 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 { String process(Object input) { return ((String) input).toUpperCase(); } } class Downcase extends Processor { String process(Object input) { return ((String) input).toLowerCase(); } } class Splitter extends Processor { String process(Object input) { return Arrays.toString(((String) input).split(" ")); } }
In this example, the Test.process method can accept an instance object of a Processor and its subclasses, and then operate on an Object's objects. According to the incoming Processor, the operations are different. This method embodies the policy design pattern. This method contains the fixed part (s) to be executed and the policy contains the changed part (p). But in this example, two things should be noted that are not relevant to this chapter: 1. You should think about why subclasses clearly do not override the name method, but the output is still like override. 2. The subclass overrides the process method, but the return value is not Object but String. The override method must be the same or its subclass as the return type of the class method.
But if we now find a series of filter classes, as follows:
class Waveform{//Representational waveform private static long counter; private final long id=counter++; public String toString(){ return "Waveform"+id; } } class Filter{//Wave filter public String name(){ return getClass().getSimpleName(); } public Waveform process(Waveform input){ return input; } } class LowPass extends Filter{ double cutoff;//Setting Upper Limit of Low Pass Filter public LowPass(double cutoff){ this.cutoff=cutoff; } public Waveform process(Waveform input){ return input; } } class HighPass extends Filter{ double cutoff;//Setting the Lower Limit of High Pass Filter public HighPass(double cutoff){ this.cutoff=cutoff; } public Waveform process(Waveform input){ return input; } } class BandPass extends Filter{ double lowCutoff,highCutoff;//Setting Upper and Lower Limits of Bandpass Filter public BandPass(double lowCut,double highCut){ this.lowCutoff=lowCut; this.highCutoff=highCut; } public Waveform process(Waveform input){ return input; } }
If you want to pass all kinds of filters to the Test.process method, then this will be blocked by the compiler, because the process method only accepts the processor class and its subclasses. If you want the test.process method that uses the policy design pattern to still accept filters, then you need to change the processor into an interface first:
interface Processor { String name() ; Object process(Object input) ; } abstract class StringProcessor implements Processor{ public String name(){ return getClass().getSimpleName(); } } class Upcase extends StringProcessor { public String process(Object input) { return ((String) input).toUpperCase(); } } class Downcase extends StringProcessor { public String process(Object input) { return ((String) input).toLowerCase(); } } class Splitter extends StringProcessor { public String process(Object input) { return Arrays.toString(((String) input).split(" ")); } } class Waveform{//Representational waveform private static long counter; private final long id=counter++; public String toString(){ return "Waveform"+id; } }
But then we found that the filter class is what we found. We can't modify the code in this class. How can we make the new Test.process accept the filter? So we can adopt the adapter design pattern. The code is as follows:
class FilterAdapter implements Processor { Filter filter; public FilterAdapter(Filter filter) { this.filter = filter; } public String name() { return filter.name(); } public Waveform process(Object input) { return filter.process((Waveform) input); } }
So the adapter is OK when it is passed in.