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

用于在Java中实施通用静态方法的接口,java,android,generics,interface,static,Java,Android,Generics,Interface,Static,我有一个Java类Model,它对远程数据库中的一些数据进行建模。我希望我的项目中的所有数据模型都能够从Map实例提供一个构建器(实际上,我正在使用Firestore的SnapshotParser解析器,但我只需要在每个模型中调用getData())。这应该类似于: public class Model { private String name; public Model(String name) { this.name = name; } public static

我有一个Java类
Model
,它对远程数据库中的一些数据进行建模。我希望我的项目中的所有数据模型都能够从
Map
实例提供一个构建器(实际上,我正在使用Firestore的
SnapshotParser
解析器,但我只需要在每个模型中调用
getData()
)。这应该类似于:

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"));
        };
    }
}
这不起作用有两个原因(Android Studio告诉我):

  • 静态
    接口方法必须具有默认实现。如果不知道
    t
    ,我就不能那样做
  • 我得到了“
    T
    无法在静态上下文中引用”错误
如果从上面的界面中删除
static
关键字,我可以做我想做的事情,但这需要我创建
模型的实际实例来获取解析器。它会起作用,但如果方法是静态的,则更有意义

有没有一种Java方法可以实现我想要的功能

编辑:我的特定用例与数据库中的文档相匹配。构建
FirestoreRecyclerOptions
对象需要解析器将键值数据转换为
模型

FirestoreRecyclerOptions<Model1> fro1 = new FirestoreRecyclerOptions.Builder<Model1>()
            .setQuery(query1, Model1.getDocParser())
            .build();
FirestoreRecyclerOptions<Model2> fro2 = new FirestoreRecyclerOptions.Builder<Model2>()
            .setQuery(query2, Model2.getDocParser())
            .build();
FirestoreRecyclerOptions fro1=新建FirestoreRecyclerOptions.Builder()
.setQuery(query1,Model1.getDocParser())
.build();
FirestoreRecyclerOptions fro2=新建FirestoreRecyclerOptions.Builder()
.setQuery(query2,Model2.getDocParser())
.build();

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

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

在Java8中,您可以通过方法引用优雅地解决这个问题。我对Android不太了解,但我相信您仍然使用Java6,所以这不起作用

无论如何,您可以有如下内容:

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();
公共T生成器(最终供应商对象){
返回供应商。get();
}
最终供应商model1BuilderSupplier=Model1Builder::new;
建筑商(1型建筑商供应商)
.setQuery(query1,Model1.getDocParser())
.build();

这并不完全是您想要的,但您尝试的方式将不起作用。

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

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

编辑:文档解析器工厂

public interface DocParserFactory {
    SnapshotParser<Model1> getModel1Parser();
    SnapshotParser<Model2> getModel2Parser();
    ...
    SnapshotParser<Model1> getModelNParser();
}
公共接口DocParserFactory{ SnapshotParser getModel1Parser(); SnapshotParser getModel2Parser(); ... SnapshotParser getModelNParser(); }

//每个getModelXParser方法的实现
类DocParserFactoryImpl{
SnapshotParser getModel1Parser(){
返回docSnapshot->{
Map data=docSnapshot.getData();
返回新模型(data.getOrDefault(“name”,“johndoe”))};
}
...
}

私有DocParserFactory DocParserFactory;
//您可以注入真实实例(DocParserFactoryImpl)或
//返回具有可预测结果的虚拟解析器的测试实例
//当您构造此对象时。
公共ThisObject(DocParserFactory DocParserFactory){
this.docParserFactory=docParserFactory;
}
...
//你的代码
公共方法(){
...
FirestoreRecyclerOptions fro1=新建
FirestoreRecyclerOptions.Builder()
.setQuery(query1,docParserFactory.getModel1Parser())
.build();
FirestoreRecyclerOptions fro2=新建
FirestoreRecyclerOptions.Builder()
.setQuery(query2,docParserFactory.getModel2Parser())
.build();
...
}

@mszymborski
静态
方法允许在以Java SE 8开始的接口中使用。它们只是没有被子类型继承。可能有两件事是重复的——既然你提到了Android Studio,我就在这里加上Android标签。第二:为什么需要将其作为静态方法?这有一种味道,因为您可能希望有一些具体定义的方法,因为您希望每种解析器类型的方法不同。您需要实例方法来对对象强制执行某些行为。@Makoto解析器的行为取决于类本身,而不是实例;我不需要它是静态的,但在我的用例中,我需要解析器,而不需要模型的实际实例@Bhesh Gurung我不明白你的评论,你能为你描述的模式举个小例子吗?以及它们如何应用于我当前的场景?我不熟悉你描述的概念。我用一个工厂示例编辑了上面的答案。我可以在接口中声明静态方法,所以我认为我使用的是Java8
public interface DocParserFactory {
    SnapshotParser<Model1> getModel1Parser();
    SnapshotParser<Model2> getModel2Parser();
    ...
    SnapshotParser<Model1> getModelNParser();
}
// 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"))};
    }

    ...

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

    ...

}