Подтвердить что ты не робот

Скрытие конструктора за статическим методом создателя?

Недавно я обнаружил интересный способ создания нового экземпляра объекта в Google Guava и Project Lombok: скрыть конструктор за статическим создателем. Это означает, что вместо new HashBiMap() вы выполняете HashBiMap.create().

Мой вопрос - почему? Какое преимущество у вас скрывает конструктор? Для меня я не вижу абсолютно никакого преимущества в этом, и, похоже, он нарушает принципы создания базовых объектов. С самого начала вы создаете объект с new Object(), а не каким-то методом Object.createMe(). Это похоже на создание метода для создания метода.

Что вы получаете от этого?

4b9b3361

Ответ 1

Существует несколько причин, по которым вы можете предпочесть статический метод factory вместо публичного конструктора. Вы можете прочитать пункт 1 в Эффективный Java, второй выпуск для более длительного обсуждения.

  • Он позволяет типу возвращаемого объектом метода отличаться от типа класса, который содержит этот метод. Фактически возвращаемый тип может зависеть от параметров. Например, EnumSet.of(E) возвращает другой тип, если тип emum имеет очень мало элементов vs, если тип перечисления имеет много элементов (Edit: в этом конкретном случае, улучшая производительность для общего случая, когда перечисление не имеет много элементов )
  • Он позволяет кэшировать. Например, Integer.valueOf(x) по умолчанию возвращает один и тот же экземпляр объекта, если он вызван несколько раз с тем же значением x, если x находится между -128 и 127.
  • Он позволяет вам иметь именованные конструкторы (которые могут быть полезны, если вашему классу нужны многие конструкторы). См., Например, методы в java.util.concurrent.Executors.
  • Это позволяет вам создать API, который концептуально прост, но на самом деле очень мощный. Например, статические методы в Collections скрывают многие типы. Вместо того, чтобы иметь класс Collections со многими статическими методами, они могли бы создать много публичных классов, но это было бы труднее для кого-то нового для понимания или запоминания языка.
  • Для типичных типов он может ограничить объем ввода, который вам нужно сделать. Например, вместо ввода List<String> strings = new ArrayList<String>() в Guava вы можете сделать List<String> strings = Lists.newArrayList() (метод newArrayList - это общий метод, и предполагается тип типичного типа).

Для HashBiMap наиболее вероятна последняя причина.

Ответ 2

Это обычно делается потому, что класс, фактически созданный методом create(), может отличаться от типа, на который вы вызываете метод. т.е. шаблон factory, где метод create() возвращает конкретный подкласс, который является подходящим с учетом текущего контекста. (Например, возвращая один экземпляр, когда среда потока является Windows, а другая - Linux).

Ответ 3

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

/**
 * A number range that can be min-constrained, max-constrained, 
 * both-constrained or unconstrained.
 */

public class Range {
  private final long min;
  private final long max;
  private final boolean hasMin;
  private final boolean hasMax;

  private Range(long min, long max, boolean hasMin, boolean hasMax) {
    // ... (private constructor that just assigns attributes)
  }

  // Static factory methods

  public static Range atLeast (long min) {
    return new Range(min, 0, true, false);
  }

  public static Range atMost (long max) {
    return new Range(0, max, false, true);
  }

  public static Range between (long min, long max) {
    return new Range(min, max, true, true);
  }

  public static Range unconstrained () {
    return new Range (0, 0, false, false);
  }
}

Вы не могли бы сделать это, используя только конструкторы, поскольку atLeast и atMost имели бы одну и ту же подпись (оба они занимают одну длинную).

Ответ 4

Это называется шаблоном метода Factory. Где Factory лежит внутри самого класса. Википедия описывает это довольно хорошо, но вот несколько фрагментов.

Методы

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

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

Ответ 5

Ну, возможно, чтобы SomeClass.create() вытащил экземпляр из кеша. new SomeClass() не будет делать этого без каких-либо махинаций.

Также было бы возможно, чтобы create() возвращало любое количество реализаций SomeClass. В принципе, тип Factory.

Ответ 6

Хотя это не применимо к этому конкретному примеру кода, практика скрытия конструктора за статическим методом Singleton Pattern. Это используется, когда вы хотите убедиться, что один экземпляр класса создан и используется повсюду.

Ответ 7

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

HashBiMap<Foo, Bar> bimap = new HashBiMap<Foo, Bar>();

HashBiMap<Foo, Bar> bimap = HashBiMap.create();

Гува также хорошо использует тот факт, что методы factory могут иметь полезные имена, в отличие от конструкторов. Рассмотрим ImmutableList.of, ImmutableList.copyOf, Lists.newArrayListWithExpectedSize и т.д.

Он также использует тот факт, что методы factory необязательно должны создавать новый объект. Например, ImmutableList.copyOf, когда задан аргумент, который сам является ImmutableList, просто вернет этот аргумент, а не выполняет фактическое копирование.

Наконец, методы ImmutableList factory возвращают (непубличные) подклассы ImmutableList, такие как EmptyImmutableList, SingletonImmutableList и RegularImmutableList в зависимости от аргументов.

Ни одна из этих вещей не возможна с конструкторами.

Ответ 8

У меня очень интересная причина, чтобы скрыть конструктор проверить его и, пожалуйста, дайте мне знать, есть ли другая альтернатива для достижения этого

enter code here

Class A
 {
   String val;
    protected A( )
     {
     }
    protected A(String val)
     {
       this.val=val;
     }
   protected void setVal( String val)
    {
       this.val=val;
    } 
   public String getVal()
    {
      return val;
    }    
}
class B extends A
  {
     B()
     {
       super();
     }     
    public val setVal(String val)
    {
      super.val=val;    
    }
} 
class C extends A
{
    C(String val)
    {
      super(val);
    }
}

Ответ 9

Некоторые основные причины

  • В первую очередь это дает вам возможность создать экземпляр другого класса ()
  • Возможность возврата null
  • Он позволяет вам вернуть уже существующий объект