【问题标题】:Java generics and reflectionJava 泛型和反射
【发布时间】:2011-12-03 22:26:01
【问题描述】:

我在写一个小课,遇到了一个问题。

这个类读取 CSV 文件,每个键只有一个值。我有

class CSVMap<T> extends AbstractMap<String,T>

我有:

public void load(String filename)
{
    try 
    {
        BufferedReader out=new BufferedReader(new FileReader(filename));
        String []tab;
        T value;

        while(out.ready());
        {
            tab=out.readLine().split(",");


        }
    } catch (IOException e) 
    {
        e.printStackTrace();
    }
}

现在我需要创建 T 的新实例以将它们放入我的地图中(我知道 T 具有接受字符串的构造函数)。

我知道 Java 中的类型擦除,所以唯一的出路是 a) 将 T.Class 传递给我的 load 方法或将其保存在我的类中的某个私有位置 或者 b) 保留一些私有对象 T 并在方法中使用它来获取 Class 对象?

我问是因为我想确定是否没有其他方法可以做到这一点。

【问题讨论】:

  • 不,这正是你应该做的——如果你环顾四周,JDK 也会做同样的事情。
  • 这就是反射是一种获取代码信息的弱方法的原因:你只能得到编译器愿意留下的东西。并且随着类型擦除,它不清楚要留下什么(原始类型?所有中间类型?)并且类文件几乎没有任何方式谈论泛型。所以,java 编译器的人决定把它排除在外。唯一真正好的“反射”方案,可以让你获得源代码的任何信息,本质上是一个编译器前端。

标签: java generics reflection csv


【解决方案1】:

您可能希望考虑另一种方法。那就是让你的 CSVMap 将一个知道如何从一个类转换为另一个类的对象作为其构造函数的参数。

例如。

public class Test {

    public static void main(String[] args) {
        CSVMapLoader<Integer> loader = new CSVMapLoader<Integer>(new IntegerParser());
        loader.load();
    }

}

class CSVMapLoader<T> {

    private final Parser<T> parser;

    public Loader(Parser<T> parser) {
        this.parser = parser;
    }

    public CSVMap<T> load() {
            // as an example of how to get your T
        T t = parser.parse("1000");
        System.out.println("t equal to 1000? "+(t.equals(1000)));
            // and instead put your real logic to load up map here
    }
}

interface Parser<T> {
    public T parse(String str);
}

class IntegerParser implements Parser<Integer> {

    public Integer parse(String str) {
        return Integer.valueOf(str);
    }
}

作为代码设计的旁注。在这种情况下,您不应该将 (Abstract)Map 子类化。您想要的是知道如何从 csv 文件加载键值对的类。 Map 界面只是在加载后存储和访问这些对。所以你真的想要一个单独的工厂类,它接受一个文件、解析器和可能的基本地图类型并为你加载地图。给您留下一个未触及的地图实例,不知道它是如何加载的。

【讨论】:

  • 是的,完全没有理由在这里使用反射。
【解决方案2】:

不,你是对的。除非您在运行时将 Class 对象传递给实例方法的实例,否则绝对无法在运行时恢复 T 的类型参数。您可以尝试的另一种策略是将工厂对象传递给 load() 或 CSVMap。这将消除从 CSVMap 了解实际类的担忧,并使其成为调用对象的责任(通过了解工厂实现)。

对于这个问题,各种库都有自己的愚蠢解决方法。 Google Guice 中包含一个特别聪明的方法。查看http://google-guice.googlecode.com/git/javadoc/com/google/inject/TypeLiteral.html。它使用参数化类在使用具体类型参数对其进行子类化时保留其类型参数这一事实。所以你可以这样做:

TypeLiteral<List<String>> typeLiteral = new TypeLiteral<List<String>>() {};
typeLiteral.getType().toString(); // returns "java.util.List<java.lang.String>"

【讨论】:

    【解决方案3】:

    不,别无他法。我会传递Class 对象

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多