【问题标题】:Force a user of my library to implement an interface or extend an abstract class强制我的库的用户实现接口或扩展抽象类
【发布时间】:2017-03-13 16:40:23
【问题描述】:

我正在开发一个 android 库 (.aar),我想知道是否有可能,如标题所示,强制用户实现一个接口或扩展我的库的一个抽象类。

我已经知道我可以在我的图书馆中学习这样的课程:

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
        }
    }
}

库会在某个时候“单独”工作,当它到达SomeWork() 时,如果接口没有实现,它会崩溃,但这只能在运行时看到。

有没有办法在编译用户的应用程序时产生这种行为?

目标是避免用户忘记他必须实现这一点而不必将其写入文档并希望用户可能会阅读它。

感谢阅读!


编辑

我认为这个问题需要一些增强和背景。 该库的目的是提供创建管理首选项的变量的类,例如:

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);
    }
}

int、string 等也是如此。 在超类中,我将每个 VarPreference 添加到 List 以使库确认所有可用变量。 注意 getter 中的 readPropFile

然后,用户像这样在他的项目中使用该库:

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);
    }
}

现在,当用户调用访问器时,readPropFile 将首先搜索 .properties 文件是否存在,如果找到 VarPreference 列表和文件属性之间的匹配项,则相应地修改首选项。然后它将删除文件,访问器将返回值。

这就是今天存在的。

现在我们希望另一个应用程序(比如 Pilot)能够获取用户应用程序(比如 Client)的 VarPreferences。两者都实现了库。

Pilot 发送一个 Intent 请求 Client 的 VarPreference 列表,额外输入 Client 的包名。 库接收意图,验证包名,如果是客户端,它会发回列表。

问题是,如果 Client 还没有启动,则 VarPreference 不存在,并且列表为空。

我需要强制用户在我的库知道的方法中创建他的 VarPreference,以便能够随时调用它,并在必要时创建用户的 VarPreferences。

希望这更清楚!


编辑

我与一位同事重新考虑了所有这些,这让我们觉得所有这些堆栈都是有偏见的。

我没有很好地解释,即使我说了,我也没有考虑到这一点:一切都需要从图书馆完成。 因此,即使我为库提供了一个接口,应用程序也必须先运行并调用这个伪装,才能让库单独工作。

我们现在正走向自省。 (这是目标,可能做不到……) 库内将有一个抽象类,带有一个抽象方法,用户将在其中放置所有 VarPreferences 创建。用户必须扩展此类并调用该方法才能创建他的 VarPreferences。 在库中,一个方法将通过自省搜索抽象类的子类,创建该子类的实例并调用将创建 VarPreferences 的方法。

【问题讨论】:

  • 如果您让SomeWork 采用VariablesInterface 参数而不是将其存储在静态字段中,这将迫使用户实现它=)
  • 此外,您现有的代码将阻止开发人员为您提供VariablesInterface 实例,因为viprivate
  • @LouisWasserman 是的,但关键是用户永远不会直接调用 SomeWork,因此他将无法提供 VariablesInterface 的实例。
  • @CommonsWare 确实,这是示例中的错字:/
  • 那么用户进行调用的任何点都应该接受 SomeInterface 参数。

标签: java android android-library aar


【解决方案1】:

我会将抽象类和接口留在主库中,并通过类加载器从另一个加载您的其余代码。 JDBC 就是这样工作的。

【讨论】:

  • 我不确定我是否理解你的解决方案,你能解释一下吗?
  • 我理解这句话的方式:“我需要强制用户在我的库知道的方法中创建他的 VarPreference”是,您将拥有一个客户端必须实现的抽象类或接口并且您不希望他实施/覆盖其他任何东西。我说的对吗?
  • 据我了解编辑 - 您想使用自省 (coderanch.com/t/521121/java/Java-Reflection-Java-Introspection) 分析库用户生成的代码。我仍然不明白的是-'强制用户实现接口/...'是什么意思?
  • 该死,我说话的时候总是忘记反射,把内省和反射混为一谈……反正我想要的理想方式是,当你用 gradle 导入库的那一刻,gradle 会大喊在你身上,你必须实现图书馆的一些东西,但那是不可能的......
【解决方案2】:

有没有办法在编译用户的应用程序时产生这种行为?

我认为没有办法强制编译失败。但是,如果您强制他们在构造函数中提供VariablesInterface,那么它将立即失败。将VariablesInterface 设为final 并仅在构造函数中对其进行初始化:

 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;
     }
     ...

如果您无法更改构造函数,那么您还可以向任何 SomeWork 公共方法添加某种配置检查方法,以确保已正确完成 vi 接线,但这需要仔细编程以确保涵盖所有公共方法。

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

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

【讨论】:

  • 我可能遗漏了一些东西,但我真的不明白这将如何阻止代码编译。
  • 好点。我已经调整了我的答案。但是,如果不指定 VariablesInterface,他们就无法实例化您的库,因此他们无法使用它。
猜你喜欢
  • 2012-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 1970-01-01
  • 1970-01-01
  • 2010-10-21
  • 1970-01-01
相关资源
最近更新 更多