Рубрики
Без рубрики

Эффективная Java! Предпочитайте Универсальные Типы

Погружение в главу 28 “Эффективная Java”. Помеченный как java, эффективный, список, архитектура.

Использовать универсальный тип в наших объявлениях довольно просто. Следующий шаг – разрешить использование дженериков в типах, которые мы создаем сами. Давайте рассмотрим следующий класс, который не использует дженерики:

// Simple stack
public class Stack {
  private Object[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_CAPACITY = 16;

  public Stack() {
    elements = new Object[DEFAULT_INTITIAL_CAPACITY];
  }

  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public Object pop() {
    if (size == 0) {
      throw new EmptyStackException();
    }
    Object result = elements[--size];
    elements[size] = null;
    result result;
  }

  public boolean isEmpty() {
    return size == 0;
  }

  private void ensureCapacity() {
    if(elements.length == size) {
      elements = Arrays.copyOf(elements, 2 * size + 1);
    }
  }
}

Этот класс определенно будет работать, но он не очень удобен и небезопасен в использовании. Чтобы сделать что-то продуктивное с этим классом, нам нужно привести все обращения, которые могут сделать наш код менее понятным, а также привести к несчастным случаям с управлением типами. Не бойтесь, мы можем просто обобщить это и устранить эти проблемы. Давайте взглянем. Главное, что нам нужно сделать, это заменить все места, которые мы упоминаем Объект с помощью E , а также вызов универсального типа в объявлении класса.

// Simple stack
public class Stack {
  private E[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_CAPACITY = 16;

  public Stack() {
    elements = new Object[DEFAULT_INTITIAL_CAPACITY];
  }

  public void push(E e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public E pop() {
    if (size == 0) {
      throw new EmptyStackException();
    }
    Object result = elements[--size];
    elements[size] = null;
    result result;
  }

  // No further changes.
}

Круто, мы закончили, верно? К сожалению, нет, так как приведенный выше код не будет компилироваться. Проблема, с которой мы сталкиваемся, заключается в том, что мы обсуждали в нашей предыдущей главе, мы пытаемся создать универсальный массив, который не разрешен из-за того, что мы не можем создавать массивы нереализуемых типов (которые являются универсальными). Есть два основных способа, которые мы могли бы выбрать для решения этой проблемы. Давайте посмотрим на них.

Первый и предпочтительный способ – создать массив Object и привести его к универсальному типу. В итоге мы получаем что-то вроде elements = (E[]) новый объект[DEFAULT_INITIAL_CAPACITY]; Теперь он будет скомпилирован, но во время компиляции нам будет выдано непроверенное предупреждение. Мы получаем это предупреждение, потому что компилятор не может гарантировать нашу безопасность типов здесь. Однако, как обсуждалось в предыдущей главе, мы можем доказать вам, что это безопасно. Если мы посмотрим через класс, мы увидим, что массив elements является закрытым и вставляет только объекты типа E так что, похоже, это безопасно. Учитывая это, мы можем добавить @SuppressWarnings("unchecked") в конструктор и не беспокоиться о предупреждении.

Второй вариант заключается в приведении каждого извлечения объекта к типу E . Это также выдаст нам непроверенное предупреждение, которое мы также можем проверить на безопасность и подавить.

Оба этих решения дают нам компилируемый код, а также эквивалентную безопасность типов. Первое, однако, более читабельно и гораздо более содержательно по сравнению с приведением при каждом доступе второго решения. Из-за этого первое решение является предпочтительным и наиболее часто используемым.

Приведенный выше пример может показаться нарушением темы нашей предыдущей главы о предпочтении списков массивам. И, действительно, это в некотором роде противоречит этому. Однако часто это требуется, поскольку списки изначально не поддерживаются в Java. Таким образом, если бы вы реализовывали что-то вроде ArrayList вам нужно будет использовать массив.

И последнее, что следует учитывать, – это то, что, хотя может быть неплохо создавать неограниченные типы в наших дженериках, иногда может потребоваться или усовершенствоваться, чтобы установить некоторые ограничения на то, какие типы принимают наши общие параметры. Например, давайте рассмотрим java.util.concurrent. Очередь задержки которая объявляется как класс DelayQueue расширяет задержку> реализует BlockingQueue расширяет задержку> реализует BlockingQueue . Это ограничивает то, что может принимать этот параметр типа, только типами, которые являются подклассами Delayed. Хотя это ограничение, оно позволяет DelayQueue вызывать методы, объявленные в типе Delayed внутри его реализации. Определенно есть что-то, что можно держать в нашем заднем кармане.

Все это говорит о том, что дженерики безопаснее и проще в использовании, чем непатентованные типы. Когда мы пишем новый код, предпочтительно сделать его таким, чтобы приведения не требовались при его использовании. Способ добиться этого часто заключается в использовании дженериков. Даже если вы уже создали тип без дженериков, вы часто можете генерировать его постфактум, не нарушая текущих обычаев.

Оригинал: “https://dev.to/kylec32/effective-java-favor-generic-types-2302”