#java #generics
#java #общие сведения
Вопрос:
Как мне вызвать, Class.forName()
если результатом является универсальный тип? Обычно я могу использовать asSubclass()
, но здесь единственный способ, который я вижу для этого, — это приведение, которое меня раздражает, когда все остальное красиво типизировано с помощью дженериков.
Сценарий выглядит примерно так:
Существует файл .jar с одной точкой входа, основным классом, который имеет main()
. Для этого требуется параметр classname (и некоторые другие, здесь не относящиеся к делу). Данный класс реализует Callable<Integer>
. Этот класс загружен, инициализирован и запущен.
Вот пример того, что мне нужно:
Class<? extends Callable<Integer>> clazz = (Class<? extends Callable<Integer>>) Class.forName(options.valueOf(className)).asSubclass(Callable.class);
Есть ли какой-нибудь способ избавиться от этого приведения?
С использованием SE6.
Ответ №1:
Сначала вы, вероятно, хотите полный универсальный Class
Class<Callable<Integer>> classCI = ...;
Тогда система типов Java не имеет проблем с
Class<? extends Callable<Integer>> clazz =
Class.forName(options.valueOf(className))
.asSubclass(classCI);
Как мы можем получить classCI
? Мы можем обмануть, не проверяя приведение
Class<Callable<Integer>> classCI = (Class<Callable<Integer>>)Callable.class;
Это по своей сути небезопасно. Должны быть внешние силы, чтобы убедиться, что className
действительно является Callable<Integer>
. Например, если это a Callable<String>
, программа выполняет все приведения без каких-либо проблем, и она запускается намного позже, когда Integer call()
вызывается, и сообщение об ошибке будет очень вводящим в заблуждение.
Это нормально, если приведение не может быть проанализировано статически для достижения успеха:
Object o = ...;
String s1 = (String)o; // may fail, no javac warning
String s2 = String.class.cast(o); // may fail, no javac warning
при условии, что при сбое приведения во время выполнения немедленно выдается исключение.
Чтобы быть типобезопасным, мы должны предварительно проверить общий тип className
@SuppressWarning( "unchecked" )
Class<? Callable<Integer>> getClass(String className)
{
Class clazz = Class.forName(className);
via reflection, check generic super interfaces of clazz
if there's no Callable<Integer> super interface
throw "className is not a Callable<Integer>"
// we have *checked*, the following cast is safe
return (Class<? Callable<Integer>>)clazz;
}
У нас есть основания исключить здесь «непроверенный», потому что реализация проверяет, чтобы убедиться, что если className
на самом деле не обозначает класс, реализующий Callable<Integer>
, он немедленно выдает исключение прямо там. Наше приведение «проверено», и программа типобезопасна.
Комментарии:
1. 1: боже, я действительно хотел бы настолько хорошо разбираться в дженериках, чтобы я мог понять, что здесь происходит.
2. К вашему сведению, класс приведения<Вызываемый<Целое число>> classCI = (Класс<Вызываемый<целое число>>)Callable.class по какой-то причине не работает. В любом случае, поскольку я проверяю вызываемое<Integer> с помощью отражения, не такая уж большая проблема, просто есть о чем подумать, если вы когда-нибудь захотите это отредактировать.