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
}

简单粗暴。

接口的用法仍是特别灵活多变的,也许还有本人没有列举的用法,欢迎你们补充。

相关文章
相关标签/搜索