Java: (Отсутствие) ошибок в универсальной типизации - PullRequest
0 голосов
/ 08 мая 2018

Я не могу понять, почему я не получаю ошибки в своем коде Java.У меня есть класс, который использует универсальный тип:

import java.util.*;  // For ArrayList
public class Hat<T>
{
  public ArrayList<T> convert(String s)
  {
    T t = (T) s;    // Cast happens here

    ArrayList<T> list = new ArrayList<T>();
    list.add(t);
    return list;
  }
}

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

Hat<Integer> h = new Hat<Integer>();
ArrayList<Integer> iList = h.convert("hello");

Это создает ArrayList изЦелые числа, у которых, так или иначе, есть Строка как элемент!Это не выдает никаких ошибок во время выполнения, даже если вы печатаете ArrayList (он печатает «[hello]»).

Я бы ожидал, что будет выдана ошибка из метода «convert».Почему этого не происходит, и возможно ли это сделать?Интересно, что это происходит, когда я пытаюсь вернуть элемент из ArrayList как целое число, но ошибка не возникает из-за метода "convert".

1 Ответ

0 голосов
/ 08 мая 2018

В Java дженерики используются только во время компиляции; они «стираются» после того, как программа проверки типов проверит программу, и не влияют на ее выполнение. В частности, во время выполнения нет никакой разницы между ArrayList<Integer> и ArrayList<String> (или ArrayList чего-либо еще, в этом отношении). После завершения проверки типов ваша программа стирается, а выполняемая программа эквивалентна:

public class Hat
{
  public ArrayList convert(String s)
  {
    Object t = s;
    ArrayList list = new ArrayList();
    list.add(t);
    return list;
  }
}

Hat h = new Hat();
ArrayList iList = h.convert("hello");

, который ведет себя так, как вы наблюдали.

Итак, вопрос в том, почему эта программа проверяет тип, когда она явно выдает неверное значение, которое утверждает, что оно является ArrayList<Integer>, но содержит строки? Разве система типов не должна отклонять подобные программы?

Ну, это так, за исключением того, что есть большая лазейка: непроверенные броски. Когда вы выполняете приведение к типу, который включает в себя универсальный - в вашем случае, строку T t = (T) s; - Java не имеет ничего во время выполнения, которое он мог бы использовать для проверки правильности приведения из-за стирания. Разработчики Java могли бы просто запретить такой тип приведения, и в этом случае ваша программа не сможет скомпилироваться.

Хотя они так не сделали. Вместо этого они решили разрешить приведение типов с использованием обобщений и уверенность в том, что программист, написавший приведение, был умнее компилятора и знал, что приведение сработает. Однако, если вы используете одно из этих приведений, все ставки отключены, и система типов может в итоге, как вы обнаружили, получить ArrayList<Integer> s, которые на самом деле содержат строки. Поэтому, чтобы предупредить вас, что нужно быть осторожным, у них был компилятор но выдает предупреждение «непроверенный актерский состав» всякий раз, когда вы пишете такой актерский состав, напоминая вам, что есть подозрительный актерский состав, и вы должны доказать, что это правильно. В основах кода, над которыми я работал, непроверенные приведения должны быть помечены @SuppressWarning и комментарием, объясняющим, почему приведение всегда допустимо.

Так что, если вы хотите, чтобы имел дело с неконтролируемыми приведениями, и вы предпочли бы выполнить проверку во время выполнения? В этом случае вам нужно самостоятельно запрограммировать проверку времени выполнения. Вы можете часто делать это с Class объектами. В вашем случае вы можете добавить дополнительный параметр Class в ваш конструктор Hat, который представляет класс, который вы ожидаете T, и использовать его для создания безопасного приведения типа, проверяемого во время выполнения:

public class Hat<T>
{
  private final Class<? extends T> expectedClass;

  public Hat(Class<? extends T> expectedClass)
  {
    this.expectedClass = expectedClass;
  }

  public ArrayList<T> convert(String s)
  {
    T t = expectedClass.cast(s);  // This cast will fail at runtime if T isn't String

    ArrayList<T> list = new ArrayList<T>();
    list.add(t);
    return list;
  }
}

Тогда ваш сайт должен измениться на:

Hat<Integer> h = new Hat<Integer>(Integer.class);
ArrayList<Integer> iList = h.convert("hello");   // throws
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...