在Map中使用泛型和对象类

ao218c7q  于 2021-08-25  发布在  Java
关注(0)|答案(1)|浏览(405)

我有以下类/接口结构(无法修改源):

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

现在我想去商店 aGenbgen 在一个 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,只需代理调用。这似乎是一种矫枉过正和丑陋的行为。我觉得应该直接做。
声明类Map Map<Class<? extends Car>, SummaryGenerator<? extends Car>> 允许切割 Class<>SummaryGenerator<> 兄弟姐妹类型。我只允许相同类型的对。

lzfw57am

lzfw57am1#

如果 Package Map并且只允许放置值为 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");
        }
    }
}

下面是上述代码的测试类。简单的 Summary 使用了包含名称字段的 Car 在其字段中返回a、b或c getXSpecifics() 方法:

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

相关问题