Безопасность типов: отключенное приведение от объекта к списку ‹MyObject›

У меня есть ListView со списком настраиваемого объекта (скажем, MyObject).

Я хочу динамически фильтровать его через EditText, поэтому мне пришлось реализовать getFilter() с помощью метода publishResults:

@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
    MyObjectAdapter.this.setItems((List<MyObject>) results.values);
    MyObjectAdapter.this.notifyDataSetChanged();
}

В этот момент Eclipse жалуется: Type safety: Unchecked cast from Object to List<MyObject>

Я уверен, что это приведение всегда будет верным, но Eclipse предлагает только добавить @SuppressWarnings("unchecked"), но я категорически против SuppressWarnings, потому что это только скрывает проблему, а не решение ...

Я пробовал добавить:

if(results.values instanceof List<MyObject>)

Но Eclipse снова жалуется, и это ничего не решает ...

Cannot perform instanceof check against parameterized type List<MyObject>. Use the form List<?>

Я знаю, что преобразование всегда будет правильным, но как правильно сделать код, чтобы убедиться, что results.values на самом деле List<MyObject>?

Заранее спасибо!


person Eloi Navarro    schedule 01.02.2013    source источник


Ответы (4)


Если все, с чем вам нужно работать, это Object, тогда вы не можете проверить во время выполнения, что у вас действительно есть List<MyObject>, потому что общий тип MyObject используется только для проверки типа во время компиляции, это не доступно во время выполнения. Вот почему вы получаете сообщение об ошибке при попытке добавить проверку instanceof.

Если вы уверены, что ваш Object действительно всегда List<MyObject>, тогда я бы сказал, что @SuppressWarnings в порядке, если вы документируете, почему вы уверены, что это не проблема.

Если вы абсолютно хотите избежать предупреждения, вы можете создать свою собственную реализацию List (скажем, MyObjectList), которая сама по себе не является универсальной, но реализует List<MyObject>. Затем вы можете выполнить instanceof проверку MyObjectList во время выполнения.

Другой вариант - проверить и преобразовать в List<?>, как подсказывает ошибка instanceof. Затем вы можете перебирать элементы в списке и проверять, действительно ли они все экземпляры MyObject, и копировать их в новый List<MyObject>.

person Medo42    schedule 01.02.2013

Что ж, наконец-то мне удалось найти решение.

Как сказал @ Medo42:

Другой вариант - проверить и преобразовать в список, как предлагает ошибка instanceof. Затем вы можете перебирать элементы в списке и проверять, действительно ли они все экземпляры MyObject, и копировать их в новый список.

Несмотря на то, что я не прошел через процесс создания совершенно нового объекта, чтобы заставить этот конкретный случай работать «без предупреждений», это было правильное направление.

Поэтому я взял идею @lokoko и использовал ее в новом методе setItems() с параметром Object вместо List<MyObject>, чтобы убедиться, что

Код результата следующий:

public void setItems(List<MyObject> var){
    this.list = var;
}

public void setItems(Object var){
    List<MyObject> result = new ArrayList<MyObject>();
    if (var instanceof List){
        for(int i = 0; i < ((List<?>)var).size(); i++){
            Object item = ((List<?>) var).get(i);
            if(item instanceof MyObject){
                result.add((MyObject) item);
            }
        }
    }
    setItems(result);
}

Спасибо всем за вашу помощь!

person Eloi Navarro    schedule 01.02.2013

Попробуйте что-то вроде этого:

List<?> result = (List<?>) results.values;
for (Object object : result) {
    if (object instanceof MyObject) {
        tempList.add((MyObject) object); // <-- add to temp
    }
}

filteredItems = tempList; // <-- set filtered
person lokoko    schedule 01.02.2013
comment
Ваше решение помогло мне понять, как решить мою проблему, спасибо! - person Eloi Navarro; 01.02.2013

Вы можете выполнить проверку перед тем, как передать ее setItems().

final Object myListObj = reuslts.values;
if(myListObj instanceof List<?>) {
    if(((List<?>)myListObj).get(0) instanceof MyObject)
        // You can safely suppress the warning here because you made sure it is a List containing MyObject
        MyObjectAdapter.this.setItems((List<? extends MyObject>) myListObj);

}

Однако вам необходимо соответствующим образом изменить setItems() метод:

public void setItems(List<? extends MyObject> list) {
    // Your code here
}
person Lawrence Choy    schedule 01.02.2013
comment
Эта проверка только гарантирует, что первым объектом в списке является MyObject, поэтому единственное, что вы показали, - это то, что список может содержать такие элементы, а не то, что на самом деле это List<MyObject> или даже List<? extends MyObject>. Скорее, вы показали, что это List<? super MyObject>. - person Medo42; 01.02.2013
comment
Ты прав. Однако List<String> никогда не может содержать элемент Integer, верно? Если MyObject не имеет подкласса, это хорошо работает. Даже если объекты внутри extends MyObject, все они содержат необходимый интерфейс или функции, которые требуются MyObjectAdapter, потому что в сигнатуре метода объявлено MyObject, а не другие подклассы. - person Lawrence Choy; 01.02.2013
comment
Технически List<String> может содержать Integer, потому что общий тип списка не применяется во время выполнения. Но даже если у вас была эта гарантия и даже если MyObject не имеет подклассов, ваше предложение недействительно. Представьте себе List<Object>, который содержит одну MyObject и одну строку. Ваша проверка приведет к тому, что это будет List<? extends MyObject>, потому что первый элемент окажется MyObject, но попытка получить второй элемент через этот приведенный список вызовет ClassCastException. - person Medo42; 01.02.2013