1

我想通过调用这个方法来获得一个序列化的对象:

ArrayList<String> myArrayList = (ArrayList<String>) getSerializedObject(ArrayList.class, "arraylist.ser");

如果指定的文件(arraylist.ser)不存在,或者与我传入的类不匹配,我想返回该类的一个新实例。

private Object getSerializedObject(Class<?> c, String filename) {
    Object serObject = null;

        try {
            if (new File(filename).exists()) {
                ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename));
                Object tempObj = in.readObject();
                if (tempObj.getClass().equals(c)) {
                    System.out.println("Loading "+filename);
                    serObject = tempObj;
                }
                in.close();
            }
        }
        catch (FileNotFoundException e) { e.printStackTrace(); }
        catch (IOException e) { e.printStackTrace(); }
        catch (ClassNotFoundException e) { e.printStackTrace(); }

        if (serObject != null) {
            return serObject;
        }
        else {
            // return new instance of Class c here
        }
}
4

3 回答 3

2

使用反射,您可以创建新的类实例。我会把你指向这个包,但你已经在使用这个Class类了。

当然,如果默认构造函数不存在,您可能必须以某种方式选择合适的构造函数。

于 2013-05-17T01:04:29.150 回答
2

如果您已经知道该类,并且假设所有类都具有相同的构造函数签名,那么您可以使用反射来完成。

以下是默认的无参数构造函数

Class<?>[] args = {};
Constructor<?> constructor = c.getConstructor(args);
Object inst = constructor.newInstance((Object[])args);
于 2013-05-17T01:05:35.277 回答
1

在您的方法中,您接受作为输入,这是由对象c表示的某种未知类型。Class<?>如果 代表的类型c有一个public无参数构造函数,那么您可以通过调用创建一个新实例:

try {
  // return c.newInstance(); -- DEPRECATED as of Java 9
  return c.getConstructor().newInstance();
} catch (Exception e) {
  // handle case of no such public, no-arg constructor
}

如果没有,那么你会得到一个异常。如果您不想交叉手指猜测,而是想找出您可以访问哪些构造函数,Reflection API 将为您提供该信息:

Constructor[] publicConstructors = c.getConstructors(); // may have .length == 0
for (Constructor ctor : publicConstructors) {
    // find the one you want
}

然后,您可以检查这些对象以查看每个对象需要哪些类型的参数。当您找到所需的构造函数时,您可以调用newInstance(Object ...)Constructor对象并传递适当的参数值。

然而,即使有了这些信息,您也需要以某种方式提供适当的值。要解决“任何类”的这个问题是极其困难的。如果您可以将所需的值限制为零或仅以已知顺序限制几个特定值,那么它会成为一个更容易解决的问题,但它会严重限制您的函数实际能够构造的对象的数量或类型。

还要记住这些事情:

您的方法接受 any Class<?>,其中包括interface诸如List. 接口不能被实例化并且没有构造函数。您的示例是通过使用具体实现来避免这种极端情况ArrayList

构造函数在 Java 中不是继承的,所以即使你验证它c是一个子类,ArrayList你也不能安全地假设它c具有与ArrayList. (也就是说,除非它真的是同一类:ArrayList.class.equals(c)。)

如果此代码被重写为仅反序列化List(或明确地ArrayList)而不是接受任何类型,那么您可以简单地调用常规ArrayList构造函数并为自己省点麻烦。

但即使您这样做,您的代码也不会验证反序列化列表是否为ArrayList<String>. 反序列化过程的结果将是ArrayList<?>, or List<?>, 或者甚至根本不是 aList的东西。假设您很幸运该文件确实包含一个ArrayList,那么知道列表“仅包含String”的唯一方法是在反序列化后验证列表中的每个对象。

泛型强制发生在编译时,运行时反序列化不会验证参数化类型是类型安全的。这就是为什么编译器会发出关于不安全强制转换的警告:

Object out = getSerializedObject(ArrayList.class, "arraylist.ser");
ArrayList<?> unboundedList = (ArrayList<?>) out;  // verifies that 'out' is really an ArrayList
ArrayList<String> myArrayList =
   (ArrayList<String>) unboundedList; // UNSAFE!  Does not verify the list's contents!
于 2013-05-17T01:48:03.970 回答