java中的接口(interface)想必大家都用过,但是如果有些刚入行的萌新问起,为什么要用接口?有什么作用?说实话,一时半会儿我也答不上来。这时候看上去稳如老狗,其实心里慌的一批的我会说,这么简单,去度娘里查下你就清楚了。然后自己就偷偷的打开电脑在浏览器里面输入www.bai...,后面的大家都懂的。
这里我就不做科普了,用过接口的老司机都说好,谁用谁知道。最几年都有人主张java要面向接口编程了,可见接口的重要性。那么今天楼主要说的这个万能接口跟java接口有啥关联了?接下来大家坐稳,我们就要开始开车了。
我们自定义一个view用来做演示,在view中点击,然后在activity中需要响应点击事件。使用姿势和步骤:
1、在自定义view中定义一个接口
2、在自定义view中定义一个接口类型的变量
3、暴露一个set方法用来传入实现接口的实现类
4、在activity中implements在自定义view中的接口,并实现方法
5、在activity中初始化自定义view,然后通过暴露的set方法传入实现体
6、在自定义view中调用接口
//这里为了简化流程就只用了button,实际可能是自定义容器view,或者更为复杂的场景需要用到接口
public class NormalButton extends AppCompatButton {
public NormalButton(Context context) {
super(context);
initEvent();
}
public NormalButton(Context context, AttributeSet attrs) {
super(context, attrs);
initEvent();
}
public NormalButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initEvent();
}
//第一步
public interface Callback{
void callback();
}
//第二步
private Callback callback;
//第三步
public void setCallback(Callback callback){
this.callback = callback;
}
private void initEvent(){
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//第六步
if(callback != null){
callback.callback();
}
}
});
}
}
activity中使用
public class MainActivity extends AppCompatActivity implements NormalButton.Callback{
private NormalButton nb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
//第五步,暴露实现体
private void initView(){
nb = findViewById(R.id.nb);
nb.setCallback(this);
}
private void showToast(String msg){
Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
}
//第四步,实现接口中的方法
@Override
public void callback() {
showToast("activity 中调用普通接口");
}
}
xml很简单,我这里就不贴了。当点击界面上的button的时候,就能调用activity中的方法,并且弹出信息。
看到上一点,一切看上去似乎都非常的美好,代码简洁,逻辑清晰。你要这么想,说明你还是太年轻。试想一下某个场景,项目中需要针对不同的业务和场景设计大量的接口,这样你就需要定义许多个接口,然后并且要实现他们。随之而来的问题就是接口取名字难,一个类可能大量的implements(android系统源码中就有很多这样的例子),导致代码冗余,而且由于需要持有实现类的引用,增加了项目的耦合度。
总结下缺点和不足主要为两点:
1、代码冗余
2、耦合度高
我们的万能接口其实还是借鉴了传统接口的思路,利用面向对象的思想,对传统接口来做抽象,以达到减少代码冗余和低耦合的目标。
首先我们看下接口都有哪些特征,然后我们给它抽象出来。
1、接口名(所有接口必须都有的)
2、未实现函数体(可以为空,个人感觉为空意义不大,只是语法可以这样允许)
3、函数体参数(可选)
4、函数体返回值(可选)
所以我们的基类必须要有一个属性,就是接口名字,定义如下:
//基类
public abstract class Function {
private String mFunctionName;
public String getmFunctionName() {
return mFunctionName;
}
public Function(String mFunctionName){
this.mFunctionName = mFunctionName;
}
}
针对上面总结的特征,我们可以组合成多种形式,比如没有参数和没有返回值的,有参数没有返回值的等等类型,接下来我们来一一抽象出来,能不能看懂暂且后面再说。
没有参数和没有返回值
public abstract class FunctionNoParmNoResult extends Function {
public FunctionNoParmNoResult(String mFunctionName) {
super(mFunctionName);
}
//没有实现的函数体,具体实现交给子类
public abstract void function();
}
没有参数有返回值
public abstract class FunctionNoParmWithResult extends Function{
public FunctionNoParmWithResult(String mFunctionName) {
super(mFunctionName);
}
//返回值类型使用泛型
public abstract Result function();
}
有多个参数没有返回值
public abstract class FunctionWithMultiParmNoResult extends Function{
public FunctionWithMultiParmNoResult(String mFunctionName) {
super(mFunctionName);
}
//多个参数用可变数组,由于多个参数类型可能不统一,所以用object类型
public abstract void function(Object ... param);
}
有多个参数有返回值
public abstract class FunctionWithMultiParmWithResult extends Function{
public FunctionWithMultiParmWithResult(String mFunctionName) {
super(mFunctionName);
}
//返回值用泛型,可变参数
public abstract Result function(Object ... param);
}
有单个参数没有返回值
public abstract class FunctionWithParmNoResult extends Function{
public FunctionWithParmNoResult(String mFunctionName) {
super(mFunctionName);
}
//单个参数用泛型传入
public abstract void function(Param param);
}
有单个参数有返回值
public abstract class FunctionWithParmWithResult extends Function{
public FunctionWithParmWithResult(String mFunctionName) {
super(mFunctionName);
}
//泛型参数和泛型返回结果
public abstract Result function(Param param);
}
可能看到这里,很多同学还是不太明白为什么要这样子写,看不明白没关系,我们先放着,看完下面的再回过头来看就清楚了。
很显然,我们需要一个管理类来统一管理上面的这个不同类型的“接口”。那管理类应该怎么设计了?
1、针对不同的类型,需要用不同的容器来盛放
2、提供添加“接口”到管理类里的方法
3、提供调用不同类型“接口”的方法
4、最好能支持链式调用
综上,管理类如下:
//function 管理类
public class FunctionsManger {
private static FunctionsManger _instance;
//容器用来存放函数不同类型的接口,key是函数名
private HashMap mFunctionNoParmNoResult;
private HashMap mFunctionNoParmWithResult;
private HashMap mFunctionWithParmNoResult;
private HashMap mFunctionWithParmWithResult;
private HashMap mFunctionWithMultiParmNoResult;
private HashMap mFunctionWithMultiParmWithResult;
private FunctionsManger(){
//初始化
mFunctionNoParmNoResult = new HashMap<>();
mFunctionNoParmWithResult = new HashMap<>();
mFunctionWithParmNoResult = new HashMap<>();
mFunctionWithParmWithResult = new HashMap<>();
mFunctionWithMultiParmNoResult = new HashMap<>();
mFunctionWithMultiParmWithResult = new HashMap<>();
}
//单例模式
public static FunctionsManger getInstance(){
if(_instance == null){
_instance = new FunctionsManger();
}
return _instance;
}
//无参数无返回值,添加
public FunctionsManger addFunction(FunctionNoParmNoResult func){
mFunctionNoParmNoResult.put(func.getmFunctionName(),func);
return this;
}
//无参数无返回值,调用
public void invokeFunction(String functionName){
if(TextUtils.isEmpty(functionName)){
throw new RuntimeException("Function name can not be empty");
}
if(mFunctionNoParmNoResult != null){
FunctionNoParmNoResult func = mFunctionNoParmNoResult.get(functionName);
if(func != null){
func.function();
}else{
throw new RuntimeException("Function "+functionName+" is not exits");
}
}
}
//无参数有返回值,添加
public FunctionsManger addFunction(FunctionNoParmWithResult func){
mFunctionNoParmWithResult.put(func.getmFunctionName(),func);
return this;
}
//无参数有返回值,调用
public Result invokeFunction(String functionName,Class clazz){
if(TextUtils.isEmpty(functionName)){
throw new RuntimeException("Function name can not be empty");
}
if(mFunctionNoParmWithResult != null){
FunctionNoParmWithResult func = mFunctionNoParmWithResult.get(functionName);
if(func != null){
if(clazz != null){
return clazz.cast(func.function());
}else{
return (Result)func.function();
}
}else{
throw new RuntimeException("Function "+functionName+" is not exits");
}
}
return null;
}
//有参数无返回值,添加
public FunctionsManger addFunction(FunctionWithParmNoResult func){
mFunctionWithParmNoResult.put(func.getmFunctionName(),func);
return this;
}
//有参数无返回值,调用
public void invokeFunction(String functionName,Param param){
if(TextUtils.isEmpty(functionName)){
throw new RuntimeException("Function name can not be empty");
}
if(mFunctionWithParmNoResult != null){
FunctionWithParmNoResult func = mFunctionWithParmNoResult.get(functionName);
if(func != null){
func.function(param);
}else{
throw new RuntimeException("Function "+functionName+" is not exits");
}
}
}
//有参数有返回值,添加
public FunctionsManger addFunction(FunctionWithParmWithResult func){
mFunctionWithParmWithResult.put(func.getmFunctionName(),func);
return this;
}
//有参数有返回值,调用
public Result invokeFunction(String functionName,Param param,Class clazz){
if(TextUtils.isEmpty(functionName)){
throw new RuntimeException("Function name can not be empty");
}
if(mFunctionWithParmWithResult != null){
FunctionWithParmWithResult func = mFunctionWithParmWithResult.get(functionName);
if(func != null){
if(clazz != null){
return clazz.cast(func.function(param));
}else{
return (Result) func.function(param);
}
}else{
throw new RuntimeException("Function "+functionName+" is not exits");
}
}
return null;
}
//有多个参数无返回值,添加
public FunctionsManger addFunction(FunctionWithMultiParmNoResult func){
mFunctionWithMultiParmNoResult.put(func.getmFunctionName(),func);
return this;
}
//有多个参数无返回值,调用
public void invokeFunction(String functionName,Object ... param){
if(TextUtils.isEmpty(functionName)){
throw new RuntimeException("Function name can not be empty");
}
if(mFunctionWithMultiParmNoResult != null){
FunctionWithMultiParmNoResult func = mFunctionWithMultiParmNoResult.get(functionName);
if(func != null){
func.function(param);
}else{
throw new RuntimeException("Function "+functionName+" is not exits");
}
}
}
//有多个参数有返回值,添加
public FunctionsManger addFunction(FunctionWithMultiParmWithResult func){
mFunctionWithMultiParmWithResult.put(func.getmFunctionName(),func);
return this;
}
//有多个参数有返回值,调用
public Result invokeFunction(String functionName,Class clazz,Object ... param){
if(TextUtils.isEmpty(functionName)){
throw new RuntimeException("Function name can not be empty");
}
if(mFunctionWithMultiParmWithResult != null){
FunctionWithMultiParmWithResult func = mFunctionWithMultiParmWithResult.get(functionName);
if(func != null){
if(clazz != null){
return clazz.cast(func.function(param));
}else{
return (Result) func.function(param);
}
}else{
throw new RuntimeException("Function "+functionName+" is not exits");
}
}
return null;
}
//退出应用时调用
public void clear(){
mFunctionNoParmNoResult.clear();
mFunctionNoParmWithResult.clear();
mFunctionWithParmNoResult.clear();
mFunctionWithParmWithResult.clear();
mFunctionWithMultiParmNoResult.clear();
mFunctionWithMultiParmWithResult.clear();
}
}
代码可能有点多,但是逻辑一点都不复杂,注释也写的很清楚,那接下来就是如何使用了。
定义一个自定义view模仿文章开始时提到的需要使用接口的场景
public class NoParamNoResultButton extends AppCompatButton {
//定义接口名字
public static final String NAME_NPNR = NoParamNoResultButton.class.getSimpleName()+"_npnr";
public NoParamNoResultButton(Context context) {
super(context);
initEvent();
}
public NoParamNoResultButton(Context context, AttributeSet attrs) {
super(context, attrs);
initEvent();
}
public NoParamNoResultButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initEvent();
}
private void initEvent(){
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//通过名字调用接口
FunctionsManger fm = FunctionsManger.getInstance();
fm.invokeFunction(NAME_NPNR);
}
});
}
}
然后activity使用
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFunction();
}
private void initFunction(){
//将指定名字的接口类型添加到管理类中
FunctionsManger fm = FunctionsManger.getInstance();
fm.addFunction(new FunctionNoParmNoResult(NoParamNoResultButton.NAME_NPNR) {
@Override
public void function() {
showToast("activity 中调用无参无返回值接口 :"+NoParamNoResultButton.NAME_NPNR);
}
});
}
private void showToast(String msg){
Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
}
}
怎么样,是不是达到了和文章开始说的一样的效果,但是你会发现,我们少了很多步骤,根本不需要定义interface,不需要implements,代码减少了很多,而且不需要持有实现类的引用,耦合度非常低,只需要指定一个唯一的名字就ok了。
接下来我们再看下一个稍微复杂点的例子,有多个参数和返回值
public class WithMultiParamWithResultView extends AppCompatButton {
public static final String TAG = WithMultiParamWithResultView.class.getName()+"_wmpwr";
public WithMultiParamWithResultView(Context context) {
super(context);
initEvent();
}
public WithMultiParamWithResultView(Context context, AttributeSet attrs) {
super(context, attrs);
initEvent();
}
public WithMultiParamWithResultView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initEvent();
}
private void initEvent(){
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//传入返回值类型和不同类型参数,并接收结果显示到界面上
String result = FunctionsManger.getInstance().invokeFunction(TAG,String.class,"hello world",2);
setText(result);
}
});
}
}
activity使用
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFunction();
}
private void initFunction(){
FunctionsManger fm = FunctionsManger.getInstance();
fm.addFunction(new FunctionNoParmNoResult(NoParamNoResultButton.NAME_NPNR) {
@Override
public void function() {
showToast("activity 中调用无参无返回值接口 :"+NoParamNoResultButton.NAME_NPNR);
}
}).addFunction(new FunctionWithMultiParmWithResult(WithMultiParamWithResultButton.NAME_WMPWR) {
@Override
public String function(Object... param) {
String param1 = param[0].toString();
int param2 = (Integer) param[1];
return param1 + param2;
}
});
}
private void showToast(String msg){
Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
}
}
可以看到其实相比上面,只是加了一个对多参数和有返回值类型的"接口"的链式添加,这里由于篇幅有限,其他的各种类型就不一一演示了,原理跟这个差不多。其他类型的"接口"调用,请参考我的github:https://github.com/mrcoderzou/CommInterface,欢迎star。
虽然文章中代码看上去有点多,其实逻辑都不复杂。如果有问题,欢迎留言,周末愉快!