2017-10-14 89 views
0

我试图用类型化的回调实现抽象片段来在几个子类中使用它。如何检查Fragment.onAttach()内的类型化回调类型

如何检查Context是否为适当类的实例?

我abstact CallbackFragment代码:

public abstract class CallbackFragment<C> extends Fragment { 

    protected C mCallback; 

    public CallbackFragment() { 
    } 

    @Override 
    public void onAttach(Context context) { 
     super.onAttach(context); 

     //just in case 
     if(context == null) 
      throw new NullPointerException(); 

     try { 
      mCallback = (C) context; //this line not seems to throw any exception 
     } catch (ClassCastException exception) { 
      throw new RuntimeException(context.toString() + " must implement Callbacks"); 
     } 
    } 

    @Override 
    public void onDetach() { 
     super.onDetach(); 
     mCallback = null; 
    } 
} 

车辆名单片段:

public abstract class VehicleListFragment<T extends Vehicle> 
     extends CallbackFragment<VehicleListFragment.Callback<T>> { 

    //callback for any list of any vehicle 
    public interface Callback<T extends Vehicle> { 
     void onListItemSelected(T selectedItem); 
    } 

    //common code for list of any vehicle 
    public VehicleListFragment() { 
    } 
} 

巴士,卡车,轮船,自行车,无论列表的片段:

public class BusListFragment 
    extends VehicleListFragment<Bus> { 

    //code specific for list of bus 
    public BusListFragment() { 
    } 
} 

车辆细节片段:

public abstract class VehicleDetailsFragment<T extends Vehicle, C extends VehicleDetailsFragment.Callback<T>> 
     extends CallbackFragment<C> { 

    //common methods of callback for any vehicle 
    public interface Callback<T> { 
     void onVehicleEdited(T editeItem); 
    } 

    //common code for any vehicle 
    public VehicleDetailsFragment() { 
    } 
} 

巴士,卡车,轮船,自行车,无论细节片段:

public class BusDetailsFragment 
     extends VehicleDetailsFragment<Bus, BusDetailsFragment.Callback> { 

    //specific for Bus methods 
    public interface Callback 
      extends VehicleDetailsFragment.Callback<Bus> { 
     void onSomethingSpecificForBusHappened(Bus bus); 
    } 

    //code specific for Bus 
    public BusDetailsFragment() { 
    } 
} 

我试图添加一个抽象方法CallbackFragment得到回调类:

public abstract class CallbackFragment<C> extends Fragment { 

    ... 

    @NonNull 
    protected abstract Class<C> getCallbackClass(); 

    @Override 
    public void onAttach(Context context) { 
     super.onAttach(context); 
     ... 

     //now checking instanceof like this 
     if(!getCallbackClass().isAssignableFrom(context.getClass())){ 
      throw new RuntimeException(context.toString() + " must implement Callbacks"); 
     } 
    } 
} 

随着BusDetailsFragment一切看起来不错:

public class BusDetailsFragment 
     extends VehicleDetailsFragment<Bus, BusDetailsFragment.Callback> { 

    @NonNull 
    @Override 
    protected Class<Callback> getCallbackClass() { 
     return Callback.class; 
    } 

    ... 
} 

但不能与BusListFragment:

public class BusListFragment 
     extends VehicleListFragment<Bus> { 

    @NonNull 
    @Override 
    protected Class<Callback<Bus>> getCallbackClass() { 
     /** 
     * I'm not seeing any option here 
     * 
     * mCallback - is null yet. So, there is no way to use mCallback.getClass() 
     * 
     * Callback<Bus>.class - Cannot select from parameterized type 
     */ 
     //return mCallback.getClass(); 
     //return Callback<Bus>.class; 
    } 

    ... 
} 

当然,我可以创造VehicleListFragment的每个子类,扩展VehicleListFragment.Callback的自己的接口(如在VehicleDetailsFragment的子类),但它总是这个样子:

public interface Callback 
     extends VehicleListFragment.Callback<Bus> { 
    //nothing more here 
} 

这看起来不是最适合我的选择。 也许还有其他解决方案?请分享您的想法。任何帮助,将不胜感激。

回答

2
mCallback = (C) context; //this line not seems to throw any exception 

这个调用永远不会抛出异常。在运行期间,您的C被替换为Object(这就是所谓的类型擦除) - 并且一切都是Object。因此,您可以在此指定任何内容。

要具有该点的异常(或至少错误确定),在你需要它,你可以使用:

public abstract class CallbackFragment<C> extends Fragment { 

    protected C mCallback; 
    protected Class<C> callbackClass; 

    public CallbackFragment(Class<C> clazz) { 
     this.callbackClass = clazz; 
    } 

    @Override 
    public void onAttach(Context context) { 
     super.onAttach(context); 

     //just in case 
     if(context == null) 
      throw new NullPointerException(); 

     if (clazz.isAssignableFrom(context.getClass()){ 
      mCallback = (C) context; 
     }else{ 
      //oops 
     } 
    } 
} 

OFC。那么你的FragmentCreation将改变从

CallbackFragment<Something> fragment = new CallbackFragment<Something>(); 

CallbackFragment<Something> fragment = new CallbackFragment<Something>(Something.class); 

这是一个有点不同,但可以让你保持实际类型的轨道在任何时间,绕过类型擦除。

PS:对于继承类,你可以做到这一点更通用:

public abstract class CallbackFragment<C> extends Fragment { 
    protected Class<C> callbackClass; 

    public CallbackFragment() { 
      this.callbackClass = (Class<C>) ((ParameterizedType) getClass() 
         .getGenericSuperclass()).getActualTypeArguments()[0];; 
    } 
} 

public class CallbackFragmentOfSomething extends <CallbackFragment<Something>>{ 

} 

这只失败,如果你的实际的类不因继承“对飞”定义,但:

CallbackFragment<Something> fragment = new CallbackFragment<Something>(); 

(未经检验的一切/没有复制粘贴,但应该有点精确)

+0

更新了'ps' - 为一点小毛病:) – dognose

+0

哇!谢谢。我会试试这个。我只是不确定我现在已经完全理解你的答案。你有没有注意到BusListFragment的回调函数是VehicleListFragment.Callback ,我无法获得.class的参数化类型? – user2661298

+1

@ user2661298使用泛型作为具有自己类型的类型,同时继承其他类型的其他泛型时,很难跟踪泛型 - 不需要一步一步地调试。拇指规则:尽可能跟踪您的实际班级,并且您可以随时确定实际的班级类型并“保存”。 – dognose