《JAVA编程思想》学习备忘(第311页Interfaces-1)

来源:互联网 发布:网络文明ppt免费下载 编辑:程序博客网 时间:2024/05/02 01:34

Interfaces and abstract classes provide a more structured way to separate interface from implementation.

抽象类是普通类和接口的中继,它是构建一个含有些许未实现方法的类的重要和必须的工具。你不能总是使用一个纯粹的接口。

Abstract classes and methods

当你需要通过通用接口控制类集时,你创建一个抽象类。这可防止用户对实现类的不当操作。

抽象方法:仅有声明没有方法体的未实现方法。例:abstract void f();

If a class contains one or more abstract methods,the class itself must be qualified as abstract.

可以把一个未包含任何抽象方法的类作为抽象类,这非常有用,它可防止将其实例化。

 

使用抽象类和方法的例子:

public enum Note {
    MIDDLE_C, C_SHARP, B_FLAT;
}

public abstract class Instrument {
    private int i;//Storage allocated for each
    public abstract void play(Note n);
    public String what(){return "Instrument";};
    public abstract void adjust();
}

class Wind extends Instrument {

 public String what(){return "Wind";}

 public void adjust() {
 }

 public void play(Note n) {
  System.out.println("Wind.play() " + n);
 }
}

class Percussion extends Instrument{
 public String what(){return "Percussion";}

 public void adjust() {
 }

 public void play(Note n) {
  System.out.println("Percussion.play() " + n);
 }
}

class Stringed extends Instrument{
 public String what(){return "Stringed";}

 public void adjust() {
 }

 public void play(Note n) {
  System.out.println("Stringed.play() " + n);
 }
}

class Brass extends Wind{
 public void adjust() {
  System.out.println("Brass.adjust()");
 }

 public void play(Note n) {
  System.out.println("Brass.play() " + n);
 }
}

class Woodwind extends Wind{
 public void play(Note n){
  System.out.println("Woodwind.play() " + n);
 }
 public String what(){return "Woodwind";}
}
public class Music4 {
 //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 tuneAll(Instrument[] e){
  for(Instrument i : e)
   tune(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()
  };
  tuneAll(orchestra);

 }
}
输出:

Wind.play() MIDDLE_C
Percussion.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.play() MIDDLE_C
抽象类也是非常有用的重构工具。

 

Interfaces

关键字interface带给你比抽象更深一步的概念。它产生出一个完全的抽象类,完全没有实现。除了方法体,它允许创建者确定方法名称、形参列表、和返回类型。一个接口提供的仅为形式并无实现。

一个接口也可包括属性,但它们暗示为 static 和 final 类型。

使用implements关键字来实现一个特定的接口。

You can choose to explicitly declare the methods in an interface as public,but they are public even if you don't say it.So when you implement an interface,the methods from the interface must be defined as public.

例子:

public enum Note {
    MIDDLE_C, C_SHARP, B_FLAT;
}

interface Instrument {
 //Compile-time constant:
 int VALUE = 5;//static & final
 //Cannot have method definitions:
 void play(Note n);//Automatically public
 void adjust();
}

class Wind implements Instrument{
 public void adjust() {
  System.out.println(this + ".adjust()");
 }
 public String toString(){return "Wind";}
 public void play(Note n) {
  System.out.println(this + ".play() " + n);
 }
}

class Percussion implements Instrument{

 public void adjust() {
  System.out.println(this + ".adjust()");
 }
 public String toString(){return "Percussion";}
 public void play(Note n) {
  System.out.println(this + ".play() " + n);
 }
}

class Stringed implements Instrument{
 public void adjust() {
  System.out.println(this + ".adjust()");
 }
 public String toString(){return "Stringed";}
 public void play(Note n) {
  System.out.println(this + ".play() " + n);
 }
}

class Brass extends Wind{
 public String toString(){return "Brass";}
}

class Woodwind extends Wind{
 public String toString(){return "Woodwind";}
}

public class Music5 {
 // 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 tuneAll(Instrument[] e){
  for(Instrument i : e)
   tune(i);
 }
 public static void main(String[] args) {
  Instrument[] orchestra = {
    new Wind(),
    new Percussion(),
    new Stringed(),
    new Brass(),
    new Woodwind()
  };
  tuneAll(orchestra);
 }
}
输出:

Wind.play() MIDDLE_C
Percussion.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.play() MIDDLE_C


For example,supose you have a Processor class that has a name() and a process() method that takes input,modifies it and produces output.The base class is extended to create different types of Processor.In this case,the Processor subtypes modify String objects(note that the return types can be covariant,but not the argument types):

public 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){
  //The split() argument divides a String into pieces:
  return Arrays.toString(((String)input).split(" "));
 }
}
public class Apply {
 public static void process(Processor p,Object s){
  System.out.println("Using Processor " + p.name());
  System.out.println(p.process(s));
 }
 public static String s = "Disagrement with beliefs is by definition incorrect";
 public static void main(String[] args) {
  process(new Upcase(),s);
  process(new Downcase(),s);
  process(new Splitter(),s);
 }
}
输出:

Using Processor Upcase
DISAGREMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Using Processor Downcase
disagrement with beliefs is by definition incorrect
Using Processor Splitter
[Disagrement, with, beliefs, is, by, definition, incorrect]


...The split() method is part of the String class.It takes the String object and splits it using the argument as a boundary,and returns a String[].It is used here as a shorter way of creating an array of String.

 

Now suppose you discover a set of electronic filters that seem like they could fit into your Apply.process() method:

public class Waveform {
 private static long counter;
 private final long id = counter++;
 public String toString(){return "Waveform " + id;}
}
public class Filter{
 public String name(){
  return getClass().getSimpleName();
 }

 public Waveform process(Waveform input[]){return input;}

public class LowPass extends Filter {
 double cutoff;
 public LowPass(double cutoff){
  this.cutoff = cutoff;
 }
 public Waveform process(Waveform input){
  return input;
 }
}
public class HighPass extends Filter {
 double cutoff;
 public HighPass(double cutoff){
  this.cutoff = cutoff;
 }
 public Waveform process(Waveform input){
  return input;
 }
}
public class BandPass extends Filter {
 double lowCutoff,highCutoff;
 public BandPass(double lowCut,double highCut){
  lowCutoff = lowCut;
  highCutoff = highCut;
 }
 public Waveform process(Waveform input){
  return input;
 }
}


Filter has the same interface elements as Processor,but because it isn't inherited from Processor-because the creator of the Filter class had no clue you might want ot use it as a Processor-you can't use a Filter with the Apply.process() method,even though it would work fine.Basically,the coupling between Apply.process() and Process() is stronger than it needs to be,and this prevents the Apply.process() code from being reused when it ought to be.Also notice that the inputs and ouputs are both Waveforms.

 

if Processor is an interface,however,the constraints are loosened enough that you can reuse an Apply.process() that interface.Here are the modified versions of Procesor and Apply:

public interface Processor {
 String name();
 Object process(Object input);
}
public class Apply {
 public static void process(Processor p,Object s){
  System.out.println("Using Processor " + p.name());
  System.out.println(p.process(s));
 }
}
The first way you can reuse code is if client programmers can write their classes to conform to the interface,like this:

public abstract class StringProcessor implements Processor {

 public String name() {
  return getClass().getSimpleName();
 }
 
 public abstract String process(Object name);
 
 public static String s = "If she weights the same as a duck, she's made of wood";

 public static void main(String[] args) {
  Apply.process(new Upcase(), s);
  Apply.process(new Downcase(), s);
  Apply.process(new Splitter(), s);
 }
}

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(" "));
 }
}
输出:

Using Processor Upcase
IF SHE WEIGHTS THE SAME AS A DUCK, SHE'S MADE OF WOOD
Using Processor Downcase
if she weights the same as a duck, she's made of wood
Using Processor Splitter
[If, she, weights, the, same, as, a, duck,, she's, made, of, wood]


However,you are often in the situation of not being able to modify the classes that you want to use.In the case of the electronic filters,for example,the library was discovered rather than created.In these cases,you can use the Adapter design pattern.In Adapter,you write code to take the interface that you have and produce the interfaces that you need,like this:

public 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);
 }
}

public class FilterProcessor {
 public static void main(String[] args) {
  Waveform w = new Waveform();
  Apply.process(new FilterAdapter(new LowPass(1.0)),w);
  Apply.process(new FilterAdapter(new HighPass(2.0)), w);
  Apply.process(new FilterAdapter(new BandPass(3.0,4.0)), w);
 }
}

输出:

Using Processor LowPass
Waveform 0
Using Processor HighPass
Waveform 0
Using Processor BandPass
Waveform 0


Decoupling interface from implementation allows an interface to be applied to multiple different implementations,and thus your code is more reusable.

原创粉丝点击