【问题标题】:Interface vs static abstract class, when declaring a parcelable listener接口与静态抽象类,当声明一个可打包的侦听器时
【发布时间】:2025-12-01 02:20:03
【问题描述】:

我有一个DialogFragment,它提供了一个Parcelable 侦听器,因此当用户单击其中一个按钮关闭对话框时,调用者可以收到回调(它需要是Parcelable 才能存储在参数Bundle)。

起初,我将其声明为interface,如下所示:

public class EditTrackDialogFragment extends AppCompatDialogFragment {

    public interface OnEditTrackDialogListener extends Parcelable{
        public abstract void onSave(TrackData trackData);
        public abstract void onCancel(TrackData trackData);
    }

    // ...

}

...然后像这样实现它:

public class MainActivity extends AppCompatActivity {

    // ...

    private EditTrackDialogFragment.OnEditTrackDialogListener onEditTrackDialogListener = new EditTrackDialogFragment.OnEditTrackDialogListener() {
        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {

        }

        @Override
        public void onSave(TrackData trackData) {
            Log.v(TAG, "Track saved");
        }

        @Override
        public void onCancel(TrackData trackData) {
            Log.v(TAG, "Edit cancelled");
        }
    };

}

但我认为实例化器需要处理Parcelable 接口的实现似乎并不正确。所以我尝试将监听器声明为一个抽象类,它本身实现了Parcelable,如下所示:

public class EditTrackDialogFragment extends AppCompatDialogFragment {

    public static abstract class OnEditTrackDialogListener implements Parcelable{
        public abstract void onSave(TrackData trackData);
        public abstract void onCancel(TrackData trackData);

        public OnEditTrackDialogListener() {

        }

        protected OnEditTrackDialogListener(Parcel in) {
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
        }

        @Override
        public int describeContents() {
            return 0;
        }
    }

    // ...

}

...并提供了这样的具体实现:

public class MainActivity extends AppCompatActivity {

    // ...

    private EditTrackDialogFragment.OnEditTrackDialogListener onEditTrackDialogListener = new EditTrackDialogFragment.OnEditTrackDialogListener() {
        @Override
        public void onSave(TrackData trackData) {
            Log.v(TAG, "Track saved");
        }

        @Override
        public void onCancel(TrackData trackData) {
            Log.v(TAG, "Edit cancelled");
        }
    };

}

在我看来,这更干净,而且感觉更合乎逻辑的是,提供侦听器“接口”的DialogFragment 是应该处理Parcelable 实现的人。否则我每次实现监听器时都必须提供这些实现,这看起来很愚蠢,因为它们总是相同的,更不用说代码变得更加混乱了。

无论如何,我的问题是哪种方法是“最佳实践”?我设法挖掘出的所有答案似乎都坚持第一种方法,但是有什么好的理由不采用(在我看来“更好”)第二种方法吗?

【问题讨论】:

    标签: java android interface listener abstract-class


    【解决方案1】:

    一件事:

    如果您从抽象类继承,您将不会摆脱 Parcelable 实现,因为您仍然需要 CREATOR :)

    在我看来,如果您从抽象 parcelable 创建大量继承类,您的方法可以节省您的时间 - 面向对象语言的目标之一是重用 :)

    我喜欢少做些工作,所以我创建了一些抽象的包裹

    我可以与你分享矿山的例子:

    https://gist.github.com/c3ph3us/496d82751de74cae46f6d2aad2f11e71

    你确定吗?它似乎工作得很好。此外,在对此答案的评论中,有人说界面不需要创建者;不包含任何成员变量的抽象侦听器类是否仍然需要它? – BadCash

    用创建者实现抽象包裹的示例:

    https://gist.github.com/c3ph3us/1ca07c497492b322bf02d01906796471

    是的 - 您需要在抽象类的实现中提供创建者

    public interface Parcelable {
    
     ...
    
     /**
     * Interface that must be implemented and provided as a public CREATOR
     * field that generates instances of your Parcelable class from a Parcel.
     */
    public interface Creator<T> {
        /**
         * Create a new instance of the Parcelable class, instantiating it
         * from the given Parcel whose data had previously been written by
         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
         * 
         * @param source The Parcel to read the object's data from.
         * @return Returns a new instance of the Parcelable class.
         */
        public T createFromParcel(Parcel source);
    
        /**
         * Create a new array of the Parcelable class.
         * 
         * @param size Size of the array.
         * @return Returns an array of the Parcelable class, with every entry
         * initialized to null.
         */
        public T[] newArray(int size);
    }
    

    你能解释一下为什么我需要实现 Creator 接口吗?没有它似乎工作得很好? – BadCash

    https://developer.android.com/reference/android/os/Parcelable.html https://developer.android.com/reference/android/os/Parcelable.Creator.html

    请做一些研究 :) 试试这个,如果你能做到的话给我一个提示 :) 没有创造者

     public class ClassC implements Parcelable  {
         // no CREATOR 
    
        String test
    
        public ClassC(Parcel parcel) {
             test = parcel.readString();
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(test)
        }
    }
    

    classA - Activity onCreate

     Context.startActivity(new Intent(Context,ClassB.class)
                                 .putExtra("KEY", new ClassC("TEST"))
                          );
    

    ClassC - Activity onCreate

        Parcelable parcelable = getIntent().getParcelableExtra("KEY");
    

    如果你通过了第一次测试然后尝试:

       Parcel parcel = Parcel.obtain();
       parcel.writeParcelable(new ClassC("TEST"),0);
       Parcelable reRead = parcel.readParcelable(ClassC.class.getClassLoader());
    

    然后给我一个结果:)

    do you know what is Parcel ? what is Binder ? 
    

    如果失败,请阅读以下内容开始自我救赎:

    /**
     * Container for a message (data and object references) that can
     * be sent through an IBinder.  A Parcel can contain both flattened data
     * that will be unflattened on the other side of the IPC (using the various
     * methods here for writing specific types, or the general
     * {@link Parcelable} interface), and references to live {@link IBinder}
     * objects that will result in the other side receiving a proxy IBinder
     * connected with the original IBinder in the Parcel.
     * ...
     **/
     public final class Parcel {
    
    
         ... 
    
        /**
         * Read and return a new Parcelable from the parcel.  The given class loader
         * will be used to load any enclosed Parcelables.  If it is null, the default
         * class loader will be used.
         * @param loader A ClassLoader from which to instantiate the Parcelable
         * object, or null for the default class loader.
         * @return Returns the newly created Parcelable, or null if a null
         * object has been written.
         * @throws BadParcelableException Throws BadParcelableException if there
         * was an error trying to instantiate the Parcelable.
         */
        @SuppressWarnings("unchecked")
        public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
           Parcelable.Creator<?> creator = readParcelableCreator(loader);
           if (creator == null) {
               return null;
           }
           if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
             Parcelable.ClassLoaderCreator<?> classLoaderCreator =
                 (Parcelable.ClassLoaderCreator<?>) creator;
             return (T) classLoaderCreator.createFromParcel(this, loader);
           }
           return (T) creator.createFromParcel(this);
        }
        ....
    }
    

    【讨论】:

    • 你确定吗?它似乎工作得很好。此外,在对this answer 的评论中,有人说interface 不需要创建者;不包含任何成员变量的抽象侦听器类是否仍然需要它?
    • 你能解释一下为什么我需要实现Creator接口吗?没有它似乎工作得很好?
    最近更新 更多