简体   繁体   中英

Force a user of my library to implement an interface or extend an abstract class

I'm developing an android library (.aar) and I was wondering if it was possible to, as the title suggest, force a user to implement an interface or extend an abstract class of my library.

I already know that I could just go with a class like this in my library :

public class MyLibrary
{
    public interface VariablesInterface
    {
        void createVariables();
    }

    private static VariablesInterface vi = null;

    public void setVariablesInterface(VariablesInterface v)
    {
        vi = v;
    }

    private static void SomeWork()
    {
        if (vi == null)
        {
            throw new RuntimeException("You noob.");
        }
        else
        {
            // do work
        }
    }
}

The library will work "alone" at some point, and when it will come to SomeWork() , if the interface isn't implemented it will crash, but this could only be seen at runtime.

Is there a way to have this behaviour when compiling the user's application ?

The goal is to avoid the user forgetting that he have to implement this without having to write it in the documentation and hope the user will probably read it.

Thanks for reading !


EDIT

I think that this question need some enhancement and background. The purpose of the library is to provide classes that create variables which manages preferences, eg :

public class VarPreferenceBoolean extends VarPreference
{
    private boolean defaultValue;

    public VarPreferenceBoolean(String key, boolean defaultValue)
    {
        super(key, true);
        this.defaultValue = defaultValue;
    }

    public void setValue(Context context, boolean value)
    {
        SharedPreferences.Editor e = context.getSharedPreferences(PropertiesManager.preferenceFileName, Context.MODE_PRIVATE).edit();
        e.putBoolean(key, value);
        e.commit();
    }

    public boolean getValue(Context context)
    {
        readPropFile(context);
        SharedPreferences sp = context.getSharedPreferences(PropertiesManager.preferenceFileName, Context.MODE_PRIVATE);
        return sp.getBoolean(key, defaultValue);
    }
}

The same goes for int, string and so on. In the super class, I add each VarPreference to a List to keep the library acknowledged of all the variables availables. Note the readPropFile inside the getter.

Then, the user use the library in his project like this :

public class Constants
{
    public static final VarPreferenceInt     FILETYPE;
    public static final VarPreferenceInt     DATAMODE;
    public static final VarPreferenceString  URL_ONLINE;
    public static final VarPreferenceBoolean UPDATING;
    public static final VarPreferenceLong    LAST_UPDATE;

    static
    {
        FILETYPE = new VarPreferenceInt("FileType", MyFile.FileType.LOCAL.getValue());
        DATAMODE = new VarPreferenceInt("DataMode", DataProvider.DataMode.OFFLINE.getValue());
        URL_ONLINE = new VarPreferenceString("UrlOnline", "http://pouetpouet.fr");
        UPDATING = new VarPreferenceBoolean("Updating", false);
        LAST_UPDATE = new VarPreferenceLong("LastUpdate", 0L);
    }
}

Now, when the user call an accessor, readPropFile will first search if a .properties file exist and modify accordingly the preferences if it found matches between the list of VarPreference and the properties of the file. Then it will delete the file and the accessor will return the value.

This is what exists today.

Now we want another application (let's say Pilot) to be able to get the VarPreferences of the user's application (let's say Client). Both implements the library.

Pilot send an Intent asking for the VarPreference list of Client, putting in extra the package name of Client. The library receive the intent, verify the packagename, if it's Client it send back the list.

Problem is, if Client hasn't started, no VarPreference exists, and the list is empty.

I need to force the user to create his VarPreference in an method that my library know, to be able to call it whenever I want, and create the VarPreferences of the user when it's necessary.

Hope this is clearer !


EDIT

I rethought about all of this with a colleague and it just hit us that all this stack is biaised.

I didn't explain well and even if I said it, I didn't take account enough of this : everything needs to be done from the library. So, even if I give an interface to the library, the application will have to run and call this affectation first in order to let the library work alone.

We are heading towards introspection now. (This is the goal, it may not be possible...) There will be an abstract class inside the library, with an abstract method where the user will place all of the VarPreferences creations. The user will have to extends this class and call the method in order to create his VarPreferences. In the library, a method will search by introspection a child of the abstract class, create an instance of this child and call the method that will create the VarPreferences.

I would leave the abstract classes and interfaces in the main library and load the rest of your code via classloader from another. JDBC works like this.

Is there a way to have this behaviour when compiling the user's application ?

I see no way to force a compilation failure. However, if you force them to supply a VariablesInterface in the constructor then it will fail immediately. Make the VariablesInterface be final and only initialize it in the constructor:

 public class MyLibrary {
     private final VariablesInterface vi;

     public MyLibrary(VariablesInterface vi) {
        if (vi == null) {
            throw new IllegalArgumentException("vi can't be null");
        }
        this.vi = vi;
     }
     ...

If you can't change the constructor then you can also add to any SomeWork public methods some sort of configuration check method to make sure the the vi wiring has properly been done but this requires careful programming to make sure all public methods are covered.

public void somePublicMethod() {
    checkWiring();
    ...
}

private void checkWiring() {
    if (vi == null) {
        throw new IllegalStateException("vi needs to be specified");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM