鉴于下面列出的跨五个类的代码,我有两个关于在编译时解析类型的问题(明确不是在运行时)。
DbIdEnumOps
中,如何验证传递的enumClassE
也符合Class<EE>
?Main
方法中的 main
中,如何让 a
推断它在哪里显示为 Integer
,而不是 Object
,而不求助于我提供显式类型提示的 b
?我花了几个小时尝试不同的切线才能让它发挥作用。下面的代码是我所能得到的最接近的代码。
类:EnumOps.java
public final class EnumOps<E extends Enum<E>> {
private final Class<E> enumClass;
private final List<E> enumsValues;
private EnumOps(Class<E> enumClass) {
this.enumClass = enumClass;
this.enumsValues = Collections.unmodifiableList(Arrays.asList(enumClass.getEnumConstants()));
}
public static <E extends Enum<E>> EnumOps<E> from(Class<E> enumClass) {
return new EnumOps<>(enumClass);
}
public Class<E> getEnumClass() {
return this.enumClass;
}
public List<E> toList() {
return this.enumsValues;
}
//omitted lots of other helpful enum utilities that ought to have been provided by the Java compiler by default
}
接口:DbIdEnum.java
public interface DbIdEnum<T> {
T getDbId();
}
类:DbIdEnumOps.java
public final class DbIdEnumOps<E extends Enum<E>, EE extends DbIdEnum<T>, T> {
private final Map<T, E> enumValueByDbId;
private final Map<String, E> enumValueByNameLowerCaseOrDbId;
private DbIdEnum<T> toDbIdEnum(E e) {
return ((DbIdEnum<T>) e);
}
public static <E extends Enum<E>, EE extends DbIdEnum<T>, T> DbIdEnumOps<E, EE, T> from(Class<E> enumClassE) {
return new DbIdEnumOps<>(enumClassE);
}
private DbIdEnumOps(Class<E> enumClassE) {
//how to validate AT COMPILE TIME `enumClassE` also conforms to `Class<EE>`?
var enumOpsList =
EnumOps.from(Objects.requireNonNull(enumClassE))
.toList();
this.enumValueByDbId =
Collections.unmodifiableMap(
enumOpsList
.stream()
.map(e -> Map.entry(toDbIdEnum(e).getDbId(), e))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
this.enumValueByNameLowerCaseOrDbId =
Collections.unmodifiableMap(
enumOpsList
.stream()
.flatMap(e -> Stream.of(
Map.entry(e.name().toLowerCase(), e),
Map.entry(toDbIdEnum(e).getDbId().toString(), e)))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
}
public Map<T, E> getEnumValueByDbId() {
return this.enumValueByDbId;
}
public Map<String, E> getEnumValueByNameLowerCaseOrDbId() {
return this.enumValueByNameLowerCaseOrDbId;
}
}
枚举:TrafficLight.java
public enum TrafficLight implements DbIdEnum<Integer> {
GREEN(1),
YELLOW(2),
RED(3);
private final Integer dbId;
TrafficLight(Integer dbId) {
this.dbId = dbId;
}
private static final EnumOps<TrafficLight> enumOps =
EnumOps.from(TrafficLight.class);
private static final DbIdEnumOps<TrafficLight, DbIdEnum<Integer>, Integer> dbIdEnumOpsA =
DbIdEnumOps.from(TrafficLight.class);
public static EnumOps<TrafficLight> ops() {
return enumOps;
}
public static DbIdEnumOps<TrafficLight, DbIdEnum<Integer>, Integer> dbIdOps() {
return dbIdEnumOpsA;
}
public Integer getDbId() {
return this.dbId;
}
}
类:Main.java
public class Main {
public static void main(String[] args) {
//a is inferred as: DbIdEnumOps<TrafficLight, DbIdEnum<Object>, Object>
var a = DbIdEnumOps.from(TrafficLight.class);
var b = DbIdEnumOps.<TrafficLight, DbIdEnum<Integer>, Integer>from(TrafficLight.class);
DbIdEnumOps<TrafficLight, DbIdEnum<Integer>, Integer> c = DbIdEnumOps.from(TrafficLight.class);
System.out.println("use me to set a breakpoint to examine the contents of the maps");
//How do I get `a` to infer where it shows as `Integer`, instead of `Object`, without resorting to `b` where I provide an explicit type hint?
}
}
对于 1,我采取的切线涉及将同一个
enum
类两次传递给 from
方法,其中方法的签名如下所示:
public static <E extends Enum<E>, EE extends DbIdEnum<T>, T> DbIdEnumOps<E, EE, T> from(Class<EE> enumClassEe, Class<E> enumClassE) {
return new DbIdEnumOps<>(enumClassEe, enumClassE);
}
private DbIdEnumOps(Class<EE> enumClassEe, Class<E> enumClassE) {...
虽然我做到了这一点,但仅仅为了满足这两种类型而两次传递枚举看起来并不正确。
我认为有一些神秘或不寻常的类型规范路径仍然超出了我的理解。任何有关这方面的指导将不胜感激。
背景:对于那些对上下文更好奇的人,我正在尝试干燥(不要重复自己)一堆在多个代码库中使用的
enum
。许多(但不是全部)枚举用于数据库列。因此,EnumOps
和 DbIdEnumOps
之间的关注点明确(且必需)分离。
我们可以通过
&
限制泛型类型以符合多种实现。如果我们希望某个 E
成为 Enum<E>
并实现一些 interface Foo
,我们可以写 E extends Enum<E> & Foo
。当然,Foo
也可以有一个通用参数,我们可以绑定它(例如E extends Enum<E> & Foo<Integer>
)或引入一个额外的通用参数(I, E extends Enum<E> & Foo<I>
)。把它们放在一起,我们得到:
class Ideone {
public static void main(String[] args) {
boo(Bar.bar1);
// foo(new Baz()); // should not work - does not work
// foo(Bang.bang1); // should not work - does not work
}
public static <T extends Enum<T> & Foo<Integer>> void boo(T t) {
System.out.println(t.ordinal());
System.out.println(t.foo());
}
}
interface Foo<T> {
T foo();
}
enum Bar implements Foo<Integer> {
bar1(1);
final int foo;
Bar(int foo) {
this.foo = foo;
}
@Override
public Integer foo() {
return foo;
}
}
class Baz implements Foo<Integer> {
@Override
public Integer foo() {
return 42;
}
}
enum Bang {
bang1
}
Ideone.com
演示