Безопасность типов: непроверенное приведение из Object в ArrayList<MyVariable>

Вот часть программы, которая отправляет ArrayList с сервера на клиент. Я хочу удалить предупреждение о последней строке в этом коде:

Код клиента:

Socket s;
(...)
// A server is sending a list from the other side of the link.
ois = new ObjectInputStream(s.getInputStream());
MyList = (ArrayList<MyVariable>) ois.readObject();

MyVariable - это класс Java с некоторыми атрибутами. Сервер создает ArrayList и заполняет его переменными MyVariable в качестве элементов. Затем он отправляет полный список клиенту.

Я хотел бы знать, почему у меня есть предупреждение и как правильно кодировать, чтобы иметь 0 предупреждений. Если это возможно, я бы хотел избежать использования "@SuppressWarnings("unchecked")".;)

Спасибо,

Луис

4 ответа

Попробуй это

Object obj = ois.readObject();
// Check it's an ArrayList
if (obj instanceof ArrayList<?>) {
  // Get the List.
  ArrayList<?> al = (ArrayList<?>) obj;
  if (al.size() > 0) {
    // Iterate.
    for (int i = 0; i < al.size(); i++) {
      // Still not enough for a type.
      Object o = al.get(i);
      if (o instanceof MyVariable) {
        // Here we go!
        MyVariable v = (MyVariable) o;
        // use v.
      }
    }
  }
}

Невозможно избежать этого предупреждения. readObject() возвращает объект. Вы должны разыграть это. И приведение к универсальному типу всегда будет генерировать такое предупреждение.

Если вы хотите сделать свой код как можно более чистым, что является хорошей идеей, вы должны соблюдать соглашения об именах Java, и имена переменных должны начинаться со строчной буквы.

Я столкнулся с проблемой, аналогичной OP, и нашел хорошее решение с помощью комбинации комментариев из @VGR и метода Java 1.8 для массивов.

Я предоставлю свой ответ с точки зрения вопроса ОП, поэтому он является общим и, надеюсь, поможет другим:

  1. Вместо того, чтобы возвращать коллекцию (список), возвращайте массив с сервера. Преобразуйте коллекцию в массив, используя следующий код на стороне сервера:

    myVariableList.toArray(new MyVariable[0]);

    Если производительность является проблемой, описанной выше, можно использовать следующее, так что размер массива не нужно изменять:

    myVariableList.toArray(myVariableList.size());

  2. На стороне клиента преобразуйте массив объекта в массив класса MyVariable.

    Это специфично для JAVA 8.

    MyVariable[] myVarArr = Arrays.stream(ois.readObject()).toArray(MyVariable[]::new);

  3. Затем, наконец, преобразовать массив в коллекцию (список).

    List<MyVariable> myList = Arrays.asList(myVarArr);

Благодарю.

Мне это не нравится, но вы можете иметь контейнер (вроде псевдонима или typedef):

// add "implements Serializable" in your case
private static class MyVariableList {
    public List<MyVariable> value;
}

И работать с MyVariableList вместо. Таким образом, вы явно предоставляете компилятору достаточно информации для проверки типов во время выполнения.

Я тоже столкнулся с подобной ситуацией и смог ее решить. Мое решение, примененное к примеру ОП, таково:

myList = (ArrayList<Someclass>) Arrays.asList( (Someclass[]) ois.readObject() );

Я изменил соглашения об именах (как уже было предложено кем-то) на стандартную Java (объекты начинаются со строчных букв), и я переименовал класс MyVariable в Someclass чтобы было ясно, что это действительно относится к любому классу (а не только к переменным). Я также предполагаю, что соответствующий объект myList на стороне сервера типа ArrayList<Someclass> был записан в поток как массив Someclass[], Обратите внимание, что это легко сделать и аналогично первой части того, что уже предлагал абхишек, но мое решение отличается от последнего шага тем, что:

  1. Я избегаю звонить Arrays.stream
  2. Его легче читать и быстро воспринимать как простое (без дальнейшей логики) и checked (не генерирует предупреждение).
Другие вопросы по тегам