【问题标题】:Java - How to pass unknown implementation of interface to generic methodJava - 如何将未知的接口实现传递给泛型方法
【发布时间】:2021-07-20 20:15:41
【问题描述】:

我有这样的问题(当然这只是一个例子)。我有处理器,我必须修理汽车。我有一个字符串作为汽车对象的 json。我不知道 ServiceStation 的实现,我只有汽车实现类。我必须将 json 解析为汽车对象并将其提供给服务站。有没有合适的解决方案来实现这一点?

public interface Car {}

public class BMW implements Car {}

public interface ServiceStation<T extends Car> {
    public void repair(T car)
}

public class BMWServiceStation<BMW> {
    public void repair(BMW car) { ... }
}

public class ServiceStationProcessor {

    public void process(String carJson, Settings settings) {
        Class<? extends Car> carClass = settings.getCarClass();
        ServiceStation serviceStation = settings.getServiceStation();

        JavaType javaType = objectMapper.getTypeFactory().constructType(carClass);

        Object<? extends Car> obj = objectMapper.readValue(carJson, javaType); <-- ?? something like this?
        serviceStation.repair(obj)
    }

}

【问题讨论】:

  • 如果你问我认为你在问什么,答案是“不”。 Java 泛型是不可引用的。这意味着代码无法区分维修宝马和维修丰田。您应该研究实现此目标的不同方法。例如,来自 Joshua Bloch 的 Effective Java 一书中的"Type Safe Heterogeneous Container",
  • @markspace 谢谢你的建议。我会看看这个;)

标签: java json generics objectmapper


【解决方案1】:

您可以在 json 中包含一个“type”字段,用于标识具体的 Car 子类型(例如“bmw”或“mazda”)。然后你可以在 Car 接口上使用注解来告诉 Jackson 根据 'type' 值实例化什么。

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = BMW.class, name = "bmw"),
  @JsonSubTypes.Type(value = Mazda.class, name = "mazda")
})
@JsonIgnoreProperties(ignoreUnknown = true)
public interface Car {

}

然后它是类型安全的并且处理器中的逻辑简化了:

public class ServiceStationProcessor {

  public void process(String carJson, Settings settings) {
      ServiceStation serviceStation = settings.getServiceStation();
      Car obj = objectMapper.readValue(carJson, Car.class);
      serviceStation.repair(obj);
  }

}

【讨论】:

  • 这种方法的问题是你传递给serviceStation.repair()一个Car类型的对象,而我知道每个具体的Car都会以不同的方式修复。跨度>
  • @MatteoNNZ 我假设设置对象知道应用程序实例可以处理的具体汽车类型,并且 json 将只包含这些类型。如果它混合在同一个应用实例中,那么确实需要访问者。
【解决方案2】:

您可以通过以下组合达到您的结果:

  • Jackson 子类的反序列化
  • 服务站的访客模式

服务站

这是实现的“访问者”部分。

您应该有一个单独的实现,为 N 不同的具体类型公开 N 方法(而不是让 N 同一单一方法接口的不同实现)。

类似这样的:

public interface ServiceStation {

    void repair(Bmw bmw);

    void repair(Mercedes mercedes);

}

//Single implementation with a dedicated method per type of car
public class ServiceStationImpl implements ServiceStation {

    @Override
    public void repair(Bmw bmw) {
        System.out.println("Repair bmw");
    }

    @Override
    public void repair(Mercedes mercedes) {
        System.out.println("Repair mercedes");
    }
}

汽车抽象类及其实现

这将是您实现的“已访问”部分。

您将Car 定义为interface

Jackson 允许反序列化子类型,但使用 abstract class 而不是 interface

另外,我有机会添加一个接受ServiceStation 的抽象方法。这将允许每个具体的汽车实现让 ServiceStation(访问者)“访问”自己(访问者):

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes(
    {
        @JsonSubTypes.Type(value = Bmw.class, name = "Bmw"),
        @JsonSubTypes.Type(value = Mercedes.class, name = "Mercedes")
    }
)
public abstract class Car {
    public abstract void accept(ServiceStation serviceStation);
}

public final class Bmw extends Car {
    @JsonIgnore
    private final String type = "Bmw";

    @Override
    public void accept(ServiceStation serviceStation) {
        serviceStation.repair(this); //<-- will call the Bmw method of the service station
    }
}

public final class Mercedes extends Car {
    @JsonIgnore
    private final String type = "Mercedes";

    @Override
    public void accept(ServiceStation serviceStation) {
        serviceStation.repair(this); //<-- will call the Mercedes method of the service station
    }
}

主代码

至此,就变得这么简单了:

public class ServiceStationProcessor {

    public void process(String carJson, Settings settings) throws JsonProcessingException {
        Class<? extends Car> clazz = settings.getCarClass(); //<-- get the car class as you already do
        ServiceStation serviceStation = settings.getServiceStation(); //<-- get the service station
        objectMapper.readValue(carJson, clazz).accept(serviceStation); //<-- deserialize the json into the concrete car class (Jackson will pick the right type of car), then ask the car to accept the service station (the car will dispatch to the correct type)
    }

}

【讨论】:

    猜你喜欢
    • 2021-05-05
    • 2016-02-12
    • 2011-08-06
    • 2018-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多