Java接口的幾種常見用法

接口(interface)對於面向對象編程來講是一個很是重要的概念。它是一系列方法的聲明,卻沒有具體實現。有些編程語言,好比swift,把接口解釋成「協議(protocol)」,我以爲也是很是的恰當的,接口就是對方法制定的一套規範。java

本人通過從事多年的java開發工做,以及自身對面向對象編程思想的理解,總結了如下幾點接口的常見用法,但願能夠對新手的理解有所幫助。程序員

1. 做爲對類內執行流程的具體描述

這是接口的最根本的做用。有時候類內部流程的某一個環節,並非由此類本身決定的。類並不知道在這一步時具體會執行什麼內容。因此須要在此處設置一枚接口,提供對外界的擴展。數據庫

對於這個功能有一個很是好的範例。編程

一個秒錶類:用於測量執行某一段代碼執行所需的時間。swift

初步的代碼相似於下面這樣:數組

public class Stopwatch {
  
    private long deltaTime;

    public void clear() {
        deltaTime = 0L;
    }

    public long getDeltaTime() {
        return deltaTime;
    }

    public void start() {
        long startTime = System.currentTimeMillis();
        // do something
        deltaTime = System.currentTimeMillis() - startTime;
    }
}

而使用Stopwatch的代碼相似於這樣:微信

public static void main(String[] args) {
  Stopwatch stopwatch = new Stopwatch();
  stopwatch.clear();	//時間清零
  stopwatch.start();	//開始運行
  System.out.printf("time: %dms\n",stopwatch.getDeltaTime());
}

那麼咱們知道對於秒錶這個類自己,它並不能決定本身在do something那個位置將會測量什麼內容的代碼。框架

也就是說此處代碼將會由Stopwatch的外部決定。因此此處能夠商定一個協議,也就是接口。Stopwatch決定本身開放出一個接口叫NeedMonitoringProgram須要監控的程序,其中doProgram()方法就是執行須要被監控的代碼。外部在使用Stopwatch時只須要去實現NeedMonitoringProgram接口也就定義好了須要監測的代碼。編程語言

public interface NeedMonitoringProgram {
  void doProgram();
}

Stopwatch賦予NeedMonitoringProgram對象分佈式

public class Stopwatch {

    private long deltaTime;
    private NeedMonitoringProgram program;

    public void setProgram(NeedMonitoringProgram program) {
        this.program = program;
    }

    public void start() {
        long startTime = System.currentTimeMillis();
        program.doProgram();
        deltaTime = System.currentTimeMillis() - startTime;
    }

    public interface NeedMonitoringProgram {
        void doProgram();
    }
  
  //省略clear()和getDeltaTime()方法
}

模擬測量1到1000000疊加求和所需時間

Stopwatch stopwatch = new Stopwatch();
Stopwatch.NeedMonitoringProgram program = new Stopwatch.NeedMonitoringProgram() {

  @Override
  public void doProgram() {
    int sum = 0;
    int number = 1_000_000;
    for (int i = 0; i <= number; i++) {
      sum += i;
    }
    System.out.printf("sum: %d\n", sum);
  }
};
stopwatch.setProgram(program);
stopwatch.clear();
stopwatch.start();
System.out.printf("time: %dms\n",stopwatch.getDeltaTime());

假如說待測代碼是須要外部參數的,而且也須要返回給外部執行結果的。也能夠把接口方法改進一下。

添加一個Param類來存儲參數。

private Param params = new Param();

public void addParam(String key, Object value) {
  	params.put(key, value);
}

public interface NeedMonitoringProgram<RETURN_OBJECT> {
  RETURN_OBJECT doProgram(Param params);
}

public class Param extends HashMap<String, Object> {

}

start方法也能夠直接加入NeedMonitoringProgram對象,減小使用時的步驟。 而且NeedMonitoringProgram類的泛型指定了返回類型。

public <T> T start(NeedMonitoringProgram<T> program) {
  long startTime = System.currentTimeMillis();
  T returnObject = program.doProgram(params);
  deltaTime = System.currentTimeMillis() - startTime;
  return returnObject;
}

在執行start()以前使用addParam()給代碼賦值。而start()也會返回代碼執行的結果。

Stopwatch stopwatch = new Stopwatch();
stopwatch.clear();
stopwatch.addParam("number", 1_000_000);
int sum = stopwatch.start(new Stopwatch.NeedMonitoringProgram<Integer>() {

    @Override
    public Integer doProgram(Stopwatch.Param params) {
      int sum = 0;
      int number = (Integer)params.get("number");
      for (int i = 0; i <= number; i++) {
      	sum += i;
      }
      return sum;
  }
});
System.out.printf("sum: %d\n", sum);
System.out.printf("time: %dms\n",stopwatch.getDeltaTime());

運用JDK8的lambda表達式甚至能夠縮寫爲

int sum = stopwatch.start(params -> {
  int s = 0;
  int number = (Integer)params.get("number");
  for (int i = 0; i <= number; i++) {
    s += i;
  }
  return s;
});

雖然java沒有函數對象這一律念,可是使用lambda能夠充分表達一種函數式編程的思想,在這裏接口即等同於表明了一個函數對象。

以上是接口最最根本的功能,普遍運用在AWT、Swing、Android這類圖形化程序中。像XXXListener這些處理控件事件的接口都是運用了此功能。

2. 多態

能夠把具體子類對象都看成父類來看,屏蔽不一樣子類對象之間的差別。

public interface Animal {
    void sayHello();
}
public class Cat implements Animal{
    @Override
    public void sayHello() {
        System.out.println("I'm a cat.");
    }
}
public class Dog implements Animal{
    @Override
    public void sayHello() {
        System.out.println("I'm a dog.");
    }
}
public static void main(String[] args) {
  Animal[] animals = new Animal[]{new Cat(), new Dog()};
  for (Animal animal : animals) {
    animal.sayHello();
  }
}

在這裏把Cat和Dog都一視同仁的放入了Animal的數組內,而且批量處理。多態的功能其實和抽象類(abstract class)相同。

3. 用於隱藏方法的具體實現過程

一個簡單的Dao

public interface UserDao {
  
  	User findUser(int id);
  
    void addUser(User user);
    
    void updateUser(User user);
    
    void deleteUser(int id);
}

這個DaoFactory工廠類表明某一框架內部生成Dao的邏輯(超簡化)

public class DaoFactory {
  
  	public <T> T getDaoInstance(Class<T> clazz) {
     	if(clazz == UserDao.class) {
          	return new UserDaoImpl();
     	} else if(……) {
          
     	}
  	}
}

那麼咱們在使用該框架時,只須要從DaoFactory中經過Dao的類型得到該Dao的具體實現對象。

咱們只關心該Dao接口有哪些咱們能夠用的方法,而不會去關心這個Dao內部是怎麼實現增刪改查的。拿着這個Dao對象咱們就能夠去完成數據庫的操做。也就是說接口爲咱們屏蔽了方法的具體實現,屏蔽了具體的實現類中一些雜亂無章的,對於使用者來講無用的中間方法。

4. 暴露分佈式服務

public interface XXXService {
  
}

在大型網站項目中,底層的服務都是基於分佈式部署的。好比說Dubbo框架,在分佈式項目中,接口和實現是分離於兩個子項目中的。分佈式的優點在於,經過架設多臺服務提供者(provider)維持了服務提供的穩定性。只要不是全部服務提供者掛機,服務消費者(consumer)依然能夠獲得穩定的服務。對於服務消費者來講它並不關心服務是由誰提供的,只關心有哪些服務能夠用,因此接口既屏蔽了方法的具體實現,甚至還能夠接來自不一樣服務提供者的服務。在這裏,接口起到了一個協議的做用,消費者須要哪些服務,由接口描述,同時提供者根據接口實現了本身的處理服務邏輯。

5. 賦予類某種能力

咱們經常會遇到一些諸如XXXable,以able結尾的類

好比說java.io.Serializable這是賦予了類可序列化的能力

咱們本身也能夠寫給類賦予能力的接口以及實現。

作過微信支付的應該都知道,對接微信支付須要發送包含xml字符串的HTTP請求。

那麼就能夠對微信支付功能封裝一個客戶端(Client)

那麼大體步驟以下:

public class WechatClient{

    /**
     * 統一下單
     */
    public void unifiedOrder() {
        //1. 生成xml
      	//2. 發送HTTP請求
      	//3. 處理請求結果
    }
}

接下來給客戶端類賦予可發送HTTP請求的能力,聲明一個接口:

public interface HttpSendable {
	//發送GET請求
    HttpResponse getHttps(String url);
	//發送包含raw富文本的POST請求
    HttpResponse postHttps(String url, String raw);
}

HttpSendable顧名思義就是可發送HTTP請求的能力

那麼給客戶端加上這個能力

public class WechatClient implements HttpSendable

這個能力的實現可使用抽象父類實現,不會形成次要代碼污染主要類的邏輯

public abstract class HttpSender implements HttpSendable{
    @Override
    public HttpResponse getHttps(String url) {
        return null;
    }

    @Override
    public HttpResponse postHttps(String url, String raw) {
        return null;
    }
}

HttpSender就是HTTP的發送者,固然它也有發送HTTP的能力HttpSendable而後客戶端繼承了它

public class WechatClient extends HttpSender implements HttpSendable{

    /**
     * 統一下單
     */
    public void unifiedOrder() {
      	//1. 生成xml
      	String xml = "";//代碼略
      	//2. 發送HTTP請求
        HttpResponse response = super.postHttps("https://", xml);
      	//3. 處理請求結果
      	//代碼略
    }
}

像這樣寫代碼,是否是會增長代碼的可理解性了呢?

6. 做爲常量接口

通常老java程序員都喜歡這麼用,包括JDK內部也有這麼用的。把一些常量扔在接口裏面。

//申請狀態常量
public interface ApplyStatusContants {
  	public static final String STATIC_UNTREATED = "STATIC_UNTREATED";//未處理
  	public static final String STATIC_ACCEPT = "STATIC_ACCEPT"; //經過
  	public static final String STATIC_REJECT = "STATIC_REJECT"; //拒絕
}

不過自從JDK1.5之後新增了enum枚舉類型。咱們就不須要常量接口了,能夠以下方式:

public enum ApplyStatus {
  	UNTREATED, ACCEPT, REJECT
}

簡單粗暴。

接口的用法仍是特別靈活多變的,也許還有本人沒有列舉的用法,歡迎你們補充。

相關文章
相關標籤/搜索