【问题标题】:Java: Dynamic Type ContraintsJava:动态类型约束
【发布时间】:2018-11-08 09:24:43
【问题描述】:

我目前正在为我的 Java 项目开发协议处理程序。我正在尝试创建一个可维护和可扩展的 API,这意味着我不想简单地硬编码支持的值类型。

我已经开始设计一个“协议处理程序”,它给定一个(受支持类型的)值,可以根据协议规范对该值进行编码,而客户无需担心翻译过程的细节。

不同的值类型,例如字符串或整数必然有不同的编码过程,但我不希望客户担心他们可能需要编码的每种可能类型持有对不同对象的引用——正如我所说,我不希望他们担心细节。

到目前为止,我已经定义了一个“通用”DynamicHandler 类,它维护着一个“特定”类型感知的 StaticHandlers 集合:

class DynamicHandler
{
  Map<Class, StaticHandler> handlers;

  <T> void handle(T value)
  {
    if(handlers.containsKey(value.class))
      handlers.get(value.getType()).handle(value);
  }

  void <T> register(StaticHandler<T> handler)
  {
    handlers.put(T.class, handler);
  }
}

这个类的想法是,客户端只需将他们想要编码的值传递给 handle 方法,然后 DynamicHandler 查找并委托给 StaticHandler。

interface StaticHandler<T>
{
  void handle(T value);
}

这里是一些使用这个系统的客户端的例子:

class StringHandler implements StaticHandler<String>
{
  void handle(String value)
  {
    ...
  }
}

DynamicHandler handler = new DynamicHandler();
handler.register(new StringHandler());
handler.handle("Hello World!");

我有两个问题正在努力寻找自己的答案:

  1. DynamicHandler.register方法中,如何在没有T实例的情况下获取T的类型?
  2. 是否可以将 DynamicHandler 类型实现为 java.util.Map,以最大限度地与任何 3rd 方代码客户端兼容,可用于构建或以其他方式处理此类对象?

编辑:由于 DynamicHandler 本质上是一个 Map,尽管有一些通用的技巧,是否可以将其实现为:DynamicHandler implements java.util.Map&lt;...,...&gt;(我不确定 Key 和 Value 类型应该在这里)。

这是我第一次在这里提出问题,所以我希望我已经对大家足够清楚了。如果您认为有什么需要澄清的,请告诉我,我会尽力而为。

【问题讨论】:

  • 您能否尝试更清楚地说明您的第二个问题?我很难弄清楚你到底在问什么。
  • 您可以在DynamicHandler.handle() 中传入的对象上调用getClass(),而不是使用无法编译的obj.class。您假设如果传入null,则应该没有任何需要处理的内容,并且对象不会与映射中的多个键匹配。
  • @Jal 抱歉,这实际上只是我的代码中的一个错误。我将其更新为使用 getClass() 。我也澄清了我的第一个问题,明确我想知道如何在 register 方法中获取 T 的类型。
  • @Ohlec 我已经编辑了我的第二个问题,希望现在更清楚了。
  • @MattMead 您如何想象客户将其用作地图? handle() 不是已经是最方便的界面了吗?

标签: java dictionary generics


【解决方案1】:

如果null 值没有被处理,并且它被设计成永远不会有任何值属于多个类,那么你可以这样做:

public class DynamicHandler {
    Map<Class, StaticHandler> handlers;

    public <T> void handle(T value) {
        if(value != null) {
            handlers.entrySet()
                    .stream()
                    .filter(entry -> 
                        entry.getKey().isInstance(value))
                    .findAny()
                    .ifPresent(entry ->
                        entry.getValue().handle(value));
        }
    }

    public void <T> register(StaticHandler<T> handler) {
        handlers.put(handler.getHandlingClass(), handler);
    }
}


interface StaticHandler<T>
{
    void handle(T value);
    Class<T> getHandlingClass();
}

public class StringHandler implements StaticHandler<String> {
    @Override public void handle(String value) {
        ...
    }
    @Override public final Class<String> getHandlingClass() {
        return String.class;
    }
}

【讨论】:

  • @Jal 我目前正计划忽略空值,对于我的预期用例而言,“编码”任何内容似乎没有什么意义。我认为您提供的代码是解决我问题的优雅方法。我当然错过了静态处理程序的 getHandlingClass() 想法,谢谢!
  • @MattMead 在设计 API 时,肯定有很多事情需要考虑。每种方法都有其优点和缺点。最好了解不同的方法,然后从那里考虑。
【解决方案2】:
  1. 在 DynamicHandler 类中,如何在没有 T 实例的情况下获取 T 的类型?

对此的常见解决方案是传递一个Class 对象:

 void <T> register(StaticHandler<T> handler, Class<T> clazz)
  {
    handlers.put(clazz, handler);
  }

就像一个常用库中的一个例子:Gson 做了类似的事情,用GsonBuilder.registerTypeHierarchyAdapter为特定类型注册 JSON 序列化器

【讨论】:

  • 或者同理,在StaticHandler中添加一个方法,给出T的类
  • @ohlec 谢谢,我自己确实对此感到疑惑,但不确定这是否是最好或最常见的方法。当 T 隐含类型时,客户端将类型显式指定为参数似乎有点多余。我会考虑这样做。
  • @Sodala 我也想知道这种方法。我会尝试两者,看看哪个更合适。
  • @MattMead 它看起来确实是多余的,但由于类型擦除,客户端确实需要以某种方式显式提供类型(寄存器中的天气或其他方法并不重要)
  • @MattMead 我添加了一个示例链接,其中 google GSON 将这种精确模式用于非常相似的目的。
猜你喜欢
  • 2012-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-06
  • 1970-01-01
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
相关资源
最近更新 更多