【问题标题】:Changing the Type of an unknown reflected class to a generic type将未知反射类的类型更改为泛型类型
【发布时间】:2018-12-15 08:05:02
【问题描述】:

简介

我正在我的代码中实现依赖注入。

我已经到了一个点,我配置了一个名为 config.properties 的属性文件。我也知道如何将属性字符串反映到对象中。

问题

问题是当我做这个反射时,java 说类型将是Class<?>,我希望它是一个泛型类型D,所以我可以重用与这个类耦合的代码。

我在哪里

下面是代码,其中D 是我要转换到的通用对象。 ObjectImplementation 是抽象层(因此在此类上使用的每个对象都实现了该接口)。 SalesRepbyID 只是容器类的名称。 fileparser 是依赖注入的不同部分(暂时忽略它)。最后,Parse 是构造我需要转换的实例的方法。

我用谷歌搜索了标题并搜索了 Stackoverflow 结果以及外部网页。我认为我所缺少的被称为工厂模式,但我不确定如何实现这样的事情,或者我是否走在正确的轨道上。

目标

这里的目标是使用反射和泛型类型,以便可以在此容器类中构造任何实现ObjectImplementation 的类,并且可以通过属性文件而不是代码来更改要使用的实现。我在命名约定方面所做的任何愚蠢的事情请告诉我,并一如既往地感谢您的努力。

public class SalesRepbyId<D extends ObjectImplementation> implements 
DataParserImplementation<Map<String,D>> {
private FileParserImplementation<ArrayList<String[]>> FileParser;

public SalesRepbyId(FileParserImplementation<ArrayList<String[]>> FileParser){
    this.FileParser = FileParser;

}
@Override
public Map<String, D> Parse() {
    Properties prop = new Properties();
    try {
        prop.load(Data_Parser.class.getResourceAsStream("config.properties"));
    } catch (IOException e) {
        System.out.println(e + " error with config.properties I/O");
    }
    try {
        Class<?> classToUse = Class.forName(prop.getProperty("ObjectImplementation"));

//construct classToUse instances here in the method many times with type D

【问题讨论】:

  • 具体的泛型类型参数在运行时被删除。因此,由于在编译时该类是未知的,因此您无法获得 D 类型。你没有类型安全,也无法得到它。
  • 所以我想错了?错误的方式是指用错误的方法来实现我的目标
  • 不完全是。您只是不应该期望类型安全。您仍然可以使用classToUse.newInstance().parseMethod()(或任何您的方法名称)。但D 仍然未知。由于“配置”在应用程序中,您可以重新设计代码以使配置提供某种枚举,基于此您可以从编译时检查的内容中​​选择实现...
  • 您可以通过说Class.forName(...).asSubclass(ObjectImplementation.class) 获得Class&lt;? extends ObjectImplementation&gt;。这将让您创建ObjectImplementation 的实例。否则,在我看来您不应该在这里使用泛型,因为您似乎在说 D 的实际类型是由文件指定的。
  • 如果我采用您的解决方案,那么任何实现 ObjectImpl 的类。并且在配置文件中会正常工作吗?我认为我需要 D 以便我可以创建通用实例,以便可以在那里使用任何以 ObjectImpl 作为父类的类。只要我能做到,我的问题就解决了。

标签: java generics reflection dependency-injection type-conversion


【解决方案1】:

因此,在这种情况下,您可以做的是在构造 SalesRepById 时传递 Class&lt;D&gt;

class SalesRepById<D extends ...> {
    private Class<D> typeOfD;

    SalesRepById(..., Class<D> typeOfD) {
        ...
        this.typeOfD = typeOfD;
    }

    @Override
    public Map<String, D> parse() {
        try {
            Class<? extends D> typeFromFile =
                Class.forName(...).asSubclass(typeOfD);

            D instanceOfD =
                typeFromFile.getConstructor().newInstance();

        ...
    }
}

(参见Class.asSubclass,用于向下转换Class 实例。例如,如果我说Class&lt;?&gt; c = String.class;,我可以说c.asSubclass(CharSequence.class) 来获得Class&lt;? extends CharSequence&gt;。但是,Integer.asSubclass(CharSequence.class) 会抛出异常。)

这意味着您必须在编译时和对象构造时知道D 的实际类型:

SalesRepById<ActualType> srbid =
    new SalesRepById<>(..., ActualType.class);

让我感到紧张的是,您似乎在说D 的实际类型是由文件指定的。在这种情况下,我认为您不应该为D 设置类型参数。看来你应该改用这样的东西:

class SalesRepById {
    SalesRepById(...) {
        ...
    }

    @Override
    public Map<String, ObjectImplementation> parse() {
        try {
            Class<? extends ObjectImplementation> typeFromFile =
                Class.forName(...).asSubclass(ObjectImplementation.class);

            ObjectImplementation instance =
                typeFromFile.getConstructor().newInstance();

        ...
    }
}

这仍然让文件指定要使用的确切子类型,并且您不必在编译时知道它。

作为一般规则,反射和泛型不能很好地混合。反射是仅运行时的,泛型是仅编译时的。

【讨论】:

  • 我解决了我自己的问题,感谢之前的 cmets + 一些研究,然后回来看到这个。因为我基本上做了这个提议,所以我接受它,因为它非常有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-07-06
  • 1970-01-01
  • 1970-01-01
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多