Java 为类类型指定泛型
我有一个方法基本上可以处理配置类型的强制转换,但是在指定泛型类型(例如Java 为类类型指定泛型,java,generics,Java,Generics,我有一个方法基本上可以处理配置类型的强制转换,但是在指定泛型类型(例如List)时,如何处理特定类型就成了一个问题。在理想情况下,例如使用类型见证: List<String> someVal = MyConfig.SOME_VAL.<List<String>>.as(List.class);' 因此,本质上这给我留下了一个由两部分组成的问题:为什么不能将类型见证用于类上的泛型(例如List(raw)->List),以及如何在不进行额外强制转换的情况下支持检索
List
)时,如何处理特定类型就成了一个问题。在理想情况下,例如使用类型见证:
List<String> someVal = MyConfig.SOME_VAL.<List<String>>.as(List.class);'
因此,本质上这给我留下了一个由两部分组成的问题:为什么不能将类型见证用于类上的泛型(例如List(raw)
->List
),以及如何在不进行额外强制转换的情况下支持检索具有泛型边界的类?第一点特别让我困惑,因为这是完全合法的:
List<String> test = new ArrayList<>();
test = MyConfig.FRIENDLY_MOBS.as(test.getClass());
List test=newarraylist();
test=MyConfig.FRIENDLY_MOBS.as(test.getClass());
尽管它返回了一个原始类型的列表,但该行确实很糟糕(类型擦除/raw-type),因为没有检查集合类型是否真的包含字符串
test = MyConfig.FRIENDLY_MOBS.as(test.getClass());
我认为最简单的解决方案是编写一个as方法,该方法接受集合类型和元素类的class对象。请参见以下示例(在静态范围内,因此必须对其进行调整):
static List words=Arrays.asList(“你好”、“你好”、“引擎”);
静态公共Coll-as(Class您对类型见证的想法确实是可行的,但是您需要一个更好的类型见证,它不仅捕获原始类型(此处列表)还有它的泛型参数。这在Java中并不容易,因为在大多数情况下,由于类型擦除,泛型参数在运行时不可用。Java的反射API使用作为泛型类型的运行时表示的子接口,但这些接口不适合您的用途,因为它们不提供任何编译时间e类型信息
然而,通过一个技巧,你可以达到你想要的。
这个技巧基于这样一个事实:如果类(例如:MyClass
)继承自泛型类型(例如:List
),则不存在类型擦除。您可以在运行时检索MyClass
从List
继承的信息(使用该方法)
但是,对我们想要传递的实际类型进行子类化是非常不灵活的(例如,这对最终类不起作用)。因此,我们创建了一个特殊的类(通常称为TypeToken
)我们可以从中继承。TypeToken
类有一个泛型参数,在子类中我们指定要传递的类型作为该参数。当然,为每个要传递的不同值创建一个特殊类通常会非常麻烦,但幸运的是,我们可以使用匿名类来简化这一过程
综上所述,解决方案可能如下所示
我们类型令牌的定义:
public abstract class TypeToken<T> {}
并称之为:
List<Integer> list = MyConfig.SOME_VAL.as(new TypeToken<List<String>>() {});
List List=MyConfig.SOME_VAL.as(new-TypeToken(){});
注意{}
,它声明了匿名子类并避免了类型擦除
最好使用现有的类作为类型令牌,例如大类库的类(如果你还不知道这个库,也看看它提供了什么,并考虑使用它)。这个类还提供了额外的帮助方法,使它更易于使用<代码>中的令牌为< /代码>方法。(直接使用
Type
实例可能很困难)
如果您担心创建太多的类,您当然可以轻松地为常见情况(如TypeToken
等)提供一些默认实例。Guava的TypeToken
还有一种方法可用于非泛型类型,因此子类将仅限于实际需要的情况
其他项目也使用这种技巧,例如Guice with class()、Gson()和Jackson()。因此,我不会太担心子类的数量,因为它们不会使源代码变得混乱。该示例不是原始类型的列表。new ArrayList();
是一个原始类型,使用make it not a.@ElliottFrisch从#as
方法返回的是原始类型,而不是我正在将原始类型列表设置为的特定列表(我对其进行了双重检查)我认为第一部分的答案是,第二部分可能是。嗯,再看一眼,我自己的这种形式的实现似乎没有像你的例子那样的编译时错误,尽管我在这里并没有看到很大的区别:它是不同的,你希望编译时错误发生,而不是让无效的程序通过编译器可能会在某个地方导致麻烦…这是你将以艰难的方式学习的东西。这是我的观点,我想要编译时错误,但我不会通过该实现获得它们:(因为你在实现中更改了内容…没错,。它们作为静态方法单独测试时工作(使用一些随机列表),但不是在配置类本身中。为每次配置检索声明一个新的匿名类似乎有点…极端。有没有办法以某种方式适应@uberwach的解决方案?我只需在测试类中设置它,就可以轻松地让它工作(如果直接从main
调用,但是在Config
中实现时,效果不一样,我不会太担心匿名类(另请参见我的编辑)。如果您不仅希望允许硬编码类型的某个子集,而且还希望允许任意类型,那么这就是您所付出的代价。如果@uberwach的解决方案在简单情况下有效,但在您的项目中无效,请提供一个不起作用的最小示例(可能是一个新问题).有趣的是,我似乎无法使用@uberwach的解决方案,因为我在参数vs.类中使用了Class
final List<String> list = as(List.class, String.class); // works
final List<String> list = as(List.class, Integer.class); // compile error: type check
final List<Integer> list = as(List.class, String.class); // compile error: type check
final List<Integer> list = as(List.class, Integer.class); // ClassCastException
public abstract class TypeToken<T> {}
public <T> T as(TypeToken<T> typeToken) {
Type targetType = typeToken.getClass().getGenericSuperclass();
// use targetType here, e.g.
if (targetType instanceof ParameterizedType) { ... }
List<Integer> list = MyConfig.SOME_VAL.as(new TypeToken<List<String>>() {});