关于android:用于在Java中实施通用静态方法的接口

Interface to enforce generic static method in Java

我有一个Java类EDCOX1(0),它从我的远程数据库中建模一些数据。我希望项目中的所有数据模型都能够从Map实例中提供一个构建器(实际上,我使用的是带firestore的SnapshotParser解析器,但在每个模型中我只调用getData()。这应该看起来像:

1
2
3
4
5
6
7
8
9
10
public class Model {
    private String name;
    public Model(String name) { this.name = name; }
    public static SnapshotParser<Model> getDocParser() {
        return docSnapshot -> {
            Map<String, Object> data = docSnapshot.getData();
            return new Model(data.getOrDefault("name","John Doe"));
        };
    }
}

请注意,我将有几个模型(Model2Model3等),它们也需要提供这样的接口。为了实施这种行为,我创建了一个DocParserSupplier泛型类,用于我的模型类实现:

1
2
3
public interface DocParserSupplier<T> {
    static SnapshotParser<T> getDocParser();
}

这不起作用的原因有两个(正如Android Studio告诉我的那样):

  • 接口的static方法必须有默认实现。我不知道T就做不到。
  • 我得到"T不能在静态上下文中引用"的错误。

如果从上面的接口中删除static关键字,我可以做我想做的,但它需要我创建Model的实际实例来获取解析器。它可以工作,但如果该方法是静态的,则更有意义。

有Java的方式来做我想要的吗?

编辑:我的具体用例是将RecyclerView与我的数据库中的文档相匹配。构造FirestoreRecyclerOptions对象需要解析器将键值数据转换为Model

1
2
3
4
5
6
FirestoreRecyclerOptions<Model1> fro1 = new FirestoreRecyclerOptions.Builder<Model1>()
            .setQuery(query1, Model1.getDocParser())
            .build();
FirestoreRecyclerOptions<Model2> fro2 = new FirestoreRecyclerOptions.Builder<Model2>()
            .setQuery(query2, Model2.getDocParser())
            .build();


接口强制实例的行为,以便以类型安全的方式传递对具有该行为的任何对象的引用。另一方面,静态方法不属于对象的任何特定实例;类名本质上只是一个名称空间。如果要强制执行行为,则必须在某个地方创建实例(或者使用反射,如果绝对需要确保类具有特定的静态方法)。

除非这个系统将被打开进行扩展,在这里其他人可以定义他们自己的模型,否则我会说完全放弃DocParserSupplier接口,并像现在一样调用静态方法,或者将它们分解为工厂接口+实现。工厂选项很好,因为您可以用一个返回测试的虚拟解析器的伪实现替换生产实现。

编辑:文档分析器工厂

1
2
3
4
5
6
public interface DocParserFactory {
    SnapshotParser<Model1> getModel1Parser();
    SnapshotParser<Model2> getModel2Parser();
    ...
    SnapshotParser<Model1> getModelNParser();
}

1
2
3
4
5
6
7
8
9
10
11
12
// The implementation of each getModelXParser method
class DocParserFactoryImpl {

    SnapshotParser<Model1> getModel1Parser() {
        return docSnapshot -> {
            Map<String, Object> data = docSnapshot.getData();
            return new Model(data.getOrDefault("name","John Doe"))};
    }

    ...

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private DocParserFactory docParserFactory;

// You can inject either the real instance (DocParserFactoryImpl) or a
// test instance which returns dummy parsers with predicable results
// when you construct this object.
public ThisObject(DocParserFactory docParserFactory) {
    this.docParserFactory = docParserFactory;
}

...

// Your code
public void someMethod() {

    ...

    FirestoreRecyclerOptions<Model1> fro1 = new
    FirestoreRecyclerOptions.Builder<Model1>()
        .setQuery(query1, docParserFactory.getModel1Parser())
        .build();
    FirestoreRecyclerOptions<Model2> fro2 = new
    FirestoreRecyclerOptions.Builder<Model2>()
        .setQuery(query2, docParserFactory.getModel2Parser())
        .build();

    ...

}


与静态或非静态无关,这是因为如果不以某种方式传递类型参数,就无法创建泛型对象的实例。事实上,几天前我回答了一个类似的问题,当时有人想用枚举来获得所需的生成器。

简而言之,如果不以某种形式传递T,则无法编写方法 T builder(final SomeNonGenericObject object)(在本例中,是 T builder())。即使它在运行时是有意义的,如果不告诉编译器它是哪种类型,编译器也无法确定要使用哪种类型。

在Java 8中,可以用方法引用来优雅地解决这个问题。我对Android不太了解,但我相信你仍然在Java 6上,所以这是行不通的。

不管怎样,您可以有如下内容:

1
2
3
4
5
6
7
8
public <T extends AbstractBuilder> T builder(final Supplier<T> object) {
    return supplier.get();
}

final Supplier<AbstractBuilder> model1BuilderSupplier = Model1Builder::new;
builder(model1BuilerSupplier)
    .setQuery(query1, Model1.getDocParser())
    .build();

这不完全是你想要的,但是你尝试去做的方式是行不通的。