Java-метод с универсальным типом возврата
Есть ли способ в Java, чтобы возвратить разные типы с одним объявлением метода?
public Object loadSerialized(String path) {
Object tmpObject;
try {
FileInputStream fis = new FileInputStream(path);
ObjectInputStream ois = new ObjectInputStream(fis);
tmpObject = (Object) ois.readObject();
ois.close();
fis.close();
return tmpObject;
}
catch(FileNotFoundException e) {return null;}
catch(Exception e) {}
}
Я хочу, чтобы этот метод возвратил объект, и я приведу его к правильному типу при вызове функции. Это было то, что я думал, но это не работает так. Нужен ли какой-то общий тип возврата для этого? Как лучше всего решить эту проблему?
2 ответа
Чтобы сделать это безопасно, вам нужно передать желаемый тип в качестве объекта Class:
public <T> T loadSerialized(String path, Class<T> targetType) {
try (ObjectInputStream ois = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream(path)))) {
Object tmpObject = (Object) ois.readObject();
return targetType.cast(tmpObject);
} catch (FileNotFoundException e) {
return null;
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
Хотя вы могли бы написать return (T) tmpObject;
, это сгенерирует предупреждение компилятора, потому что это небезопасно: поскольку компилятор знает только, что T может быть каким-то потомком Object (или самого Object), компилятор генерирует (Object)
Это то же самое, что вообще ничего не делать. Код слепо предполагает, что возвращаемый объект имеет тип T, но если это не так, когда программа пытается вызвать метод, определенный в T, вы получите неожиданное исключение. Лучше узнать, как только вы десериализовали объект, был ли он того типа, который вы ожидали.
Подобная вещь случается, если вы делаете небезопасное наложение, скажем, на List:
List<Integer> numbers = Arrays.asList(1, 2, 3);
List<?> list = numbers;
List<String> names = (List<String>) list; // Unsafe!
String name = names.get(0); // ClassCastException - not really a String!
Вы можете использовать универсальный в вашем типе возврата. Это может выглядеть примерно так. Проще говоря, компилятор выбирает лучший тип для T
в зависимости от того, как метод был вызван. Затем приведение происходит внутри метода, а не снаружи.
Обратите внимание, что я использовал синтаксис try-with-resources, чтобы избежать путаницы с закрытием потоков.
public <T> T loadSerialized(String path) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {
return (T) ois.readObject();
}
}