解惑:实现类对象为何可赋值给Java接口变量?
这是个非常好的问题,核心其实是Java里的**多态(向上转型)**机制,咱们一步步拆解清楚:
drawable = new C()是合法的? 首先得回忆Java接口的本质:接口是一种行为规范,它只定义必须被实现的方法签名,不包含具体逻辑。当一个类通过implements关键字实现某个接口时,就相当于向编译器承诺:“我会严格遵守这个规范,实现所有要求的方法”。
回到你的代码里:
B.Drawable接口定义了show(int i)的方法规范;C类通过implements B.Drawable,完整实现了这个方法;
这时候,C的实例就同时拥有两个“身份”:它是C类型的具体对象,同时也是Drawable接口的“合规实现者”。Java允许将实现了某接口的类的实例赋值给该接口类型的变量,这就是向上转型——把子类/实现类对象,赋值给父类/接口类型的变量。
这么做的最大好处是解耦:B类只需要依赖Drawable接口(知道要调用show方法),完全不需要关心具体是哪个类实现了这个逻辑。哪怕以后把C换成D(只要D也实现Drawable),B类的代码一行都不用改。
当你调用drawable.show(this.i)时,Java的动态绑定机制会自动找到实际对象(也就是C的实例)的show方法来执行,而不是接口里的空定义。
你提到的Fragment与Activity绑定,正是这种机制的典型应用。Android里常用接口来实现Fragment和Activity的通信,避免直接持有Activity引用导致的耦合和内存泄漏。举个最常见的例子:
1. Fragment定义回调接口
public class MyFragment extends Fragment { // 定义接口,规范Activity需要实现的行为 public interface OnFragmentInteractionListener { void onDataReceived(String data); } // 接口类型的变量,用来持有Activity的引用 private OnFragmentInteractionListener mListener; @Override public void onAttach(@NonNull Context context) { super.onAttach(context); // 因为Activity是Context的子类,且实现了接口,所以可以安全转型 try { mListener = (OnFragmentInteractionListener) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " 必须实现OnFragmentInteractionListener"); } } // 当Fragment需要通知Activity时,调用接口方法 private void sendDataToActivity() { if (mListener != null) { mListener.onDataReceived("来自Fragment的数据"); } } }
2. Activity实现该接口
public class MainActivity extends AppCompatActivity implements MyFragment.OnFragmentInteractionListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 实现接口的方法,接收Fragment的通知 @Override public void onDataReceived(String data) { Toast.makeText(this, "收到Fragment数据:" + data, Toast.LENGTH_SHORT).show(); } }
这里的核心逻辑和你代码里的drawable = new C()完全一致:
MainActivity实现了OnFragmentInteractionListener接口;mListener是接口类型的变量,我们把context(也就是MainActivity的实例)转型后赋值给它;- 后续调用
mListener.onDataReceived()时,实际执行的是MainActivity里的具体实现。
你提到的“近期更新中改为将接口赋值给作为Activity实例的Context”,本质就是这种写法——利用Activity本身是Context的特性,把接口变量指向Activity实例,既实现了组件间的通信,又保持了代码的低耦合。
内容的提问来源于stack exchange,提问作者Łukasz Tulczyjew

