2

在某些情况下,我看到 Firebase 的 Java API 出现一些令人惊讶的行为:当我处理 java.lang.Long 时,它会返回 java.lang.Integer 的实例。这是一个示例 JUnit 测试用例:

public class LongsTest extends TestCase {
  public void testSetLong() throws Exception {
    Firebase firebase = new Firebase("http://www.example.com");
    firebase.addChildEventListener(new ChildEventListener() {
      @Override public void onChildRemoved(DataSnapshot s) {}
      @Override public void onChildMoved(DataSnapshot s, String p) {}
      @Override public void onChildChanged(DataSnapshot s, String p) {}
      @Override public void onChildAdded(DataSnapshot s, String p) {
        Map<String, Object> map = s.getValue(new GenericTypeIndicator<Map<String,Object>>() {});
        System.out.println(map.get("key").getClass());
      }
      @Override public void onCancelled() {}
    });
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("key", new Long(15));
    firebase.child("foo").setValue(map);
    Thread.sleep(1000);
  }
}

运行此代码打印出来class java.lang.Integer!Firebase 显然仍然知道我在其代码中的某个地方有一个 Long ,因为将主体更改为onChildAdded

Map<String,Object> map2 = (Map<String,Object>)s.getValue();
System.out.println(map2.get("key").getClass())

正确打印出来class java.lang.Long。这里发生了什么?

4

1 回答 1

6

Firebase 开发人员在这里。这实际上是一种非常微妙的行为,并且与 Firebase SDK 使用的底层 json 库 (Jackson) 有关。默认情况下,Firebase 在内部将 int 和 long 存储为 long。因此,当您要求返回数据时,默认情况下您会得到一个 Long。

但是,使用 GenericTypeIndicator 会覆盖 Firebase 的默认行为,并在尝试编组为指定类型时直接转到 Jackson 的默认行为。在这种情况下,当杰克逊遇到一个可以放入整数的数字时,它就是这样做的。当它遇到一个太大而无法放入 Integer 的数字时,它会返回一个 Long。

作为练习,尝试改变

map.put("key", new Long(15));

map.put("key", (long)Integer.MAX_VALUE + 1)

并且您应该看到生成的类是 Long。

这样做的理由是 GenericTypeIndicator 是一种覆盖 Firebase 默认值的方法。通过使用指定 Object 的 GenericTypeIndicator,您将其留给底层反序列化器来选择具体类型。在这种情况下,如果字典中有所有 Long,则可以执行 Map< String, Long >。如果没有,将您遇到的任何整数转换为长整数是安全的。不适合 Integer 的值将作为 Longs 返回。有关 Jackson 的更多信息,您可以在此处找到他们的(诚然有点稀疏)文档:http ://wiki.fasterxml.com/JacksonDocumentation

于 2013-10-10T17:45:17.020 回答