У меня есть некоторый общий код, который я не могу выяснить, как законно предотвратить получение предупреждений от; я использую @SuppressWarnings ("неконтролируемый") в настоящий момент, так как кажется, что кастинг универсального типа не может быть сделан без предупреждений.
Как я могу избавиться от аннотации?
То, что я имею:
public MyObject(SharedContext<Object> ctx) {
super(ctx); // set protected field 'context'
...
context.set("Input Fields" ,Collections.synchronizedMap(new TreeMap<String,Pair<String,Boolean>>(String.CASE_INSENSITIVE_ORDER)));
context.set("Output Fields" ,Collections.synchronizedMap(new TreeMap<String,String> (String.CASE_INSENSITIVE_ORDER)));
context.set("Event Registry",new EventRegistry(log) );
}
@SuppressWarnings("unchecked")
protected void startup() {
inputFields =(Map<String,Pair<String,Boolean>>)context.get("Input Fields" ,null);
outputFields =(Map<String,String> )context.get("Output Fields" ,null);
eventRegistry =(EventRegistry )context.get("Event Registry",null);
...
}
Защищенный переменный контекст является типом SharedContext<Object>
.
Без аннотации компилятор дает предупреждения:
...\MyClass.java:94: warning: [unchecked] unchecked cast
found : java.lang.Object
required: java.util.Map<java.lang.String,com.mycompany.Pair<java.lang.String,java.lang.Boolean>>
inputFields =(Map<String,Pair<String,Boolean>>)context.get("Input Fields" ,null);
^
...\MyClass.java:95: warning: [unchecked] unchecked cast
found : java.lang.Object
required: java.util.Map<java.lang.String,java.lang.String>
outputFields =(Map<String,String> )context.get("Output Fields" ,null);
После некоторых дополнительных исследований я считаю, что нашел разумную альтернативу, которая, по крайней мере, ограничивает аннотацию подавления только одним глобальным статическим служебным методом для выполнения неконтролируемого приведения .
Самостоятельная тестовая программа, которая следует ниже, должна быть достаточно ясной:
public class Generics
{
static public void main(String[] args) {
Generics.test();
}
static private void test() {
Map<String,Object> ctx=new TreeMap<String,Object>();
Map<String,Object> map=new TreeMap<String,Object>();
Map<String,Object> tst;
ctx.put("Test",map);
tst=uncheckedCast(ctx.get("Test"));
}
@SuppressWarnings({"unchecked"})
static public <T> T uncheckedCast(Object obj) {
return (T)obj;
}
}
В другом блоге предлагалось усовершенствовать этот служебный метод:
@SuppressWarnings("unchecked")
public static <T, X extends T> X uncheckedCast(T o) {
return (X) o;
}
принудительное использование того, что возвращается, как подкласс переданного параметра.
Предполагая, поместите uncheckedCast в класс общедоступной утилиты GenUtil, мой метод запуска в вопросе не будет выдавать (бесполезных) предупреждений и будет выглядеть так:
protected void startup() {
inputFields =GenUtil.uncheckedCast(context.get("Input Fields" ,null));
outputFields =GenUtil.uncheckedCast(context.get("Output Fields" ,null));
eventRegistry=GenUtil.uncheckedCast(context.get("Event Registry",null));
...
}
Какую версию компилятора вы используете? С компилятором Java 6 (Sun JDK windows) я не видел подробного предупреждения. Я получаю предупреждение только при использовании флага '-Xlint:unchecked'.
Попробуйте -Xlint:-unchecked и сообщите нам, решит ли это вашу проблему. Подробнее о флагах,
http://java.sun.com/javase/6/docs/technotes/tools/windows/javac.html
Как назначается переменная контекста
? Это из параметра ctx, имеющего тип:
SharedContext<Object>
?
Если да, то это ваша проблема, потому что, когда вы делаете запрос, вы не набираете то, что получаете.
Первое неконтролируемое приведение может быть устранено путем определения неуниверсального класса, расширяющего универсальный Map < String, Pair
и сохранение этого в SharedContext
вместо общего TreeMap
, например (используя ForwardingMap из Guava ):
class InputFieldMap extends ForwardingMap<String,Pair<String,Boolean>> {
private final Map<String,Pair<String,Boolean>> delegate =
Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
protected Map<String,Pair<String,Boolean>> delegate() { return delegate; }
}
// ...
context.set("Input Fields" ,Collections.synchronizedMap(new InputFieldMap()));
// ...
inputFields =(InputFieldMap)context.get("Input Fields" ,null);
outputFields =(Map<?,?> )context.get("Output Fields" ,null);
Вы можете сделать второе приведение безопасным таким же образом или (при условии, что вы только читаете карту, а не изменяете ее) использовать карту как есть (с параметрами подстановки) и преобразовать значение в строку при каждом поиске:
String bar = String.valueOf(outputFields.get("foo"));
или обернуть карту:
Map<?, String> wrappedOutputFields =
Maps.transformValues(outputFields, Functions.toStringFunction());
// ...
String bar = wrappedOutputFields.get("foo");
Вы написали объект SharedContext? Если да, то можно ли заменить обычное отображение String-> Object определенными полями?
например.
context.setInputFields(...)
context.setOutputFields(...)
context.setEventRegistry(...)
context.getInputFields()
etc.
Универсальный объект контекста для хранения всех данных всегда кажется мне не совсем идеальным решением. Особенно с дженериками и непроверенными сообщениями приведения в результате.
В качестве альтернативы вы можете создать объект-оболочку под названием SoftwareMonkeyContext, который имеет определенные методы установки / получения, как указано выше, и внутренне использует ваш метод GenUtil.uncheckedCast. Это избавит вас от необходимости использовать GenUtil.uncheckedCast в нескольких местах вашего кода.