2017-10-14 47 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