如何解决在地图中使用泛型和对象的类
我有以下类/接口结构(不能修改源):
public interface Car {}
public class CarA implements Car{
public String getASpecifics() {...}
}
public class CarB implements Car{
public String getBSpecifics() {...}
}
public class Summary {...}
我想要一种为 Summray
接口的具体实现创建 Car
的通用方法,该方法将开放添加新的实现。我的方法如下:
public CarSummarizer {
public interface SummaryGenerator<T extends Car> {
Summary generateSummary(T car);
}
static {
SummaryGenerator<CarA> aGen = c -> {... c.getASpecifics(); ...}
SummaryGenerator<CarB> bGen = c -> {... c.getBSpecifics(); ...}
}
}
现在我想将 aGen
和 bgen
存储在 Map
中。我想对其进行参数化,以便我可以提供一种唯一的公共静态方法,它接受 Car car
并基于它的类对象 (car.getClass()
) 使用正确的 SummaryGenerator
实现。这应该类似于以下内容:
public static Summary getSummaryForCar(Car car) {
return map.get(car.getClass()).generateSummary(car);
}
我不知道如何声明和实例化 Map
以使其完全类型安全(即不允许插入对 (CarC.class,SummaryGenerator<CarD>)
)。我想要这样的东西:
public static <T extends Car> Map<Class<T>,SummaryGenerator<T>> map = new LinkedHashMap<>();
static {
// after instantiation
map.put(CarA.class,aGen);
map.put(CarB.class,BGen);
}
// also support following
public static <T extends Car> void addSummaryGenerator(T car,SummaryGenerator<T> sg) {
map.put(car.getClass(),sg);
}
这行不通,因为泛型不能像在函数上那样在变量上声明。
我想我可以定义新的类 public class SummarizerStorage<T extends Car>
和里面的地方地图,然后委托调用。这似乎是一种矫枉过正和丑陋。我觉得应该以某种方式直接完成。
声明像 Map<Class<? extends Car>,SummaryGenerator<? extends Car>>
这样的映射将允许配对 Class<>
和 SummaryGenerator<>
的兄弟类型。我只想允许相同类型的对。
解决方法
如果您包装地图并且只允许放置值 SummaryGenerator<T>
与 Class<T>
键匹配的条目,则可以在编译时安全下执行此操作。要获得 SummaryGenerator
,您需要强制转换,但由于您确保只添加了实际可以强制转换为 SummaryGenerator<T>
的条目,因此这样做是安全的。如果您尝试为给定的 SummaryGenerator
实现添加不兼容的 Car
,则会导致编译错误。
public class SummaryGeneratorStorage {
private static final Map<Class<? extends Car>,CarSummarizer.SummaryGenerator<? extends Car>> map = new HashMap<>();
// provides type safety to only add a SummaryGenerator<T> for a key of Class<T>
public static <T extends Car> void add(Class<T> clazz,CarSummarizer.SummaryGenerator<T> sg) {
map.put(clazz,sg);
}
@SuppressWarnings("unchecked")
public static <T extends Car> CarSummarizer.SummaryGenerator<T> get(T car) {
// this cast is safe since the add-method only
// allows a SummaryGenerator<T> to be added for a key of Class<T>
return (CarSummarizer.SummaryGenerator<T>) map.get(car.getClass());
}
private SummaryGeneratorStorage() { }
}
为了获得 Summary
,您检索 SummaryGenerator
的注册 Car
并调用实现的 generateSummary
方法。如果没有为 SummaryGenerator
的传递实现找到 Car
,只需抛出异常或以不同方式处理它。请注意,add
的签名采用 Class
而不是 Car
对象,因为此时我们不需要实例来引用 Car
的实现.
public class CarSummarizer {
public interface SummaryGenerator<T extends Car> {
Summary generateSummary(T car);
}
static {
// here you cannot pass incompatible implementations of Car (compile-time safety)
SummaryGenerator<CarA> aGen = c -> new Summary(c.getASpecifics());
SummaryGeneratorStorage.add(CarA.class,aGen); // with variable
SummaryGeneratorStorage.add(CarB.class,c -> new Summary(c.getBSpecifics())); // direct
}
public static <T extends Car> Summary getSummary(T car) {
SummaryGenerator<T> generator = SummaryGeneratorStorage.get(car);
if (generator != null) {
return generator.generateSummary(car);
} else {
throw new IllegalArgumentException("no summary generator found");
}
}
}
这里是上面代码的测试类。使用了包含 name 字段的简单 Summary
,Car
的实现在其 getXSpecifics()
方法中返回 A、B 或 C:
public class CarSummarizerTest {
@Test
public void testCarA() {
CarA car = new CarA();
Summary summary = CarSummarizer.getSummary(car);
Assert.assertEquals(car.getASpecifics(),summary.getName());
}
@Test
public void testCarB() {
CarB car = new CarB();
Summary summary = CarSummarizer.getSummary(car);
Assert.assertEquals(car.getBSpecifics(),summary.getName());
}
@Test
public void testCarC() {
CarC car = new CarC();
Assert.assertThrows(IllegalArgumentException.class,() -> {
CarSummarizer.getSummary(car);
});
}
}
为了完整性,其他类:
public class CarA implements Car {
public String getASpecifics() { return "A"; }
}
public class CarB implements Car {
public String getBSpecifics() { return "B"; }
}
public class CarC implements Car {
public String getCSpecifics() { return "C"; }
}
public class Summary {
private final String name;
public Summary(String name) { this.name = name; }
public String getName() { return name; }
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。