我正在尝试创建类似于以下内容的类型转换器:
public interface ConvertToString<T> {
String convert (T value);
}
public class ConvertLongToString implements ConvertToString<Long> {
@Override
public String convert (Long value) {
return value.toString();
}
}
public class Foo<T> {
@Inject
@Any
private Instance<ConvertToString<T>> instance;
private T value;
public String getValue() {
return instance.get().convert(value);
}
public void setValue(T value) {
this.value = value;
}
}
在此示例中,我希望Foo<Long>
的实例知道如何通过注入正确的转换器来转换值。
这是否可以像我所展示的那样使用CDI,甚至可以通过迭代实例列表来实现?
我想要构建类似于Bean Validation框架的东西,你可以为许多类型创建相同注释的验证器(当然你有@ValidatedBy
...)
答案 0 :(得分:2)
泛型只是编译时,但据说可以解析注入点并在运行时具有类似的行为。例如:
@Inject
@ConvertToString
ConvertToStringInterface<T> converter;
@Produces
@ConvertToString
public ConvertToStringInterface produceConverter(InjectionPoint injectionPoint) {
Type type = injectionPoint.getType();
ParameterizedType parameterizedType = (ParameterizedType) type;
Type argType = parameterizedType.getActualTypeArguments()[0];
Class<?> clazz = (Class<?>) argType;
if (clazz == Long.class) {
return new ConvertLongToString();
}
}
这可能需要一些调整,但它应该有效。
答案 1 :(得分:1)
正如我上面的评论所述,我不相信认为正确的答案是正确的,即使它使用高级CDI apis来回答你。看起来正确,但实际上,我认为它不可能纠正。
另一方面,标记为减号的答案是正确的,但过于抽象而无法提供帮助。
他的制片人有一个问题。当CDI创建bean时,无法知道conrete实例返回注入点。 WELD实施者在编译时缺乏相同的信息来解决您的错误问题。 因为注入点没有指定编译类型所需的具体参数化类型。 而instance.get()api也没有任何帮助,因为它没有传递任何源类型。做完了。
所以答案看起来技术上很好,但实际上它不起作用。
我知道你可以通过多种方式解决问题。 我现在至少可以想到两个,我相信还有其他人。
你可以创建一个愚蠢的Facade转换器,它唯一知道如何为输入类型找出合适的转换器。 类似的东西:
pulblic class MyClassThatNeedsAConverter{
@Inject
MyAllMightyFacadeConverterThatKnowsAllConcreteConverters converter;
public void someMethodThatNeedsToConverSomethin(T someTypeThatCanBeConverted){
// now i need to convert this to string
String convertedString = converter.convert(someTypeThatCanBeConverted);
// contiue with my business logic
}
}
那就是程序员API。 然而,您的框架将实现所有强大的转换器外观。
public class MyAllMightyFacadeConverterThatKnowsAllConcreteConverters {
@Inject
ConvertToStringInterface<String> stringToStringConverter;
@Inject
ConvertToStringInterface<Long> longToStringConverter;
// and on you go with different concreted converters
// CDI can resolve these just fine, just make sure you do not have more than
// one candidate bean
...
public <T> String convert(T whateverTypeThatComesISwallow){
if(whateverTypeThatComesISwallow instaceof Long){
return longToStringConverter.convert((Long) whateverTypeThatComesISwallow );
}
if(whateverTypeThatComesISwallow instaceof SomeOtherType){
// same old story
}
throw new RuntimeException("You are out of luck, this type i do not know off an will not convert" + whateverTypeThatComesISwallow.getClass().getCanonincalName() );
}
}
这是一条路线。
另一种方法是你可以拥有类似转换器缓存的东西。 所以你的框架可以提供像
这样的东西@ApplicationScoped
public class MyConverterHolder{
@Inject
Instance<StringConverter<?>> allConverterInstancesKnownToTheSystem;
final Map<Class, StringConveter<?>> myCacheOfConveters = new HashMap<>();
@postConstruct
public postConstruct(){
// you do not want to this initialization all the time
// so you just do it once during construction
for(StringConveter<?> currentConverterInstance : allConverterInstancesKnownToTheSystem) {
myCacheOfConveters.put(currentConverterInstance .getSupportedInputType(), currentConverterInstance );
}
} // post construct ends here
public <T> String convert(T someTypedDataIcanSwallow){
Class<T> sourceType = someTypedDataIcanSwallow.getClass();
return myCacheOfConveters.get(sourceType).convert(someTypedDataIcanSwallow);
}
}
我倾向于更喜欢第二种方法。 当哈希映射可以为我完成工作而我不需要维护它时,我不喜欢用ifs和elses发送垃圾邮件代码。 但重点是。 除非你告诉它具体的类型,否则CDI不能给你一个具体的实现来解决你的问题。 如果您没有给出具体类型,CDI可以为您提供符合某些类型标准的所有实例。但是你必须解决挑选正确问题的问题。
因此,您需要在转换之间创建此Facade对象。
关于参数化tpye注射的良好读数当然是规范本身。 这描述了用于将bean解析为注入点的匹配规则,并在描述之后给出了示例。 例如。参见&#34; 5.2.4节。原始类型和参数化类型的可分配性&#34;。
For example, Dao is eligible for injection to any injection point of
type @Default Dao<Order>, @Default Dao<User>, @Default Dao<?>,
@Default Dao<? extends Persistent> or @Default Dao<X extends
Persistent> where X is a type variable.
这一段也很重要:
Legal Bean类型中定义的任何合法bean类型都可以是 所需的注射点类型。此外,所需的类型 注入点可以包含通配符类型参数。但是,一个 type变量不是合法的注入点类型。
如果注入点类型是类型变量,则为容器 自动检测问题并将其视为定义错误。
亲切的问候。
答案 2 :(得分:0)
泛型是一个编译时构造,使其适用于运行时注入所需的信息不存在,类型擦除只是打败了整个事物....而且,我尝试使用Spring!