Такая ковариантность на дженериках не поддерживается, но можно на самом деле сделать это с массивами:
object[] a = new string[] {"spam", "eggs"};
C# выполняет проверки на этапе выполнения, чтобы препятствовать тому, чтобы Вы поместили, скажем, int
в a
.
Разница между вашим кодом и «образцом кода» заключается в том, что ваш синглтон создается при загрузке класса, тогда как в «образцовой» версии он не создается до тех пор, пока он действительно не понадобится .
Ну, в последнем случае одноэлементный объект создается прежде, чем он когда-либо понадобится, но в большинстве случаев это, вероятно, не так уж и плохо.
Кстати, Джошуа Блох рекомендует (в Эффективная Java , 2-е изд., П. 3) реализовать синглтоны с использованием одноэлементного перечисления:
public enum SingletonObject {
INSTANCE;
}
Он дает следующее обоснование:
[...] он более лаконичен, обеспечивает сериализацию машины бесплатно и предоставляет железная гарантия от нескольких создание экземпляра, даже перед лицом сложная сериализация или отражение атак. Пока это подход еще не получил широкого распространения, одноэлементный тип перечисления - лучший способ реализовать синглтон.
Я бы сказал, что последний код на самом деле является более стандартным шаблоном. Ваша первая версия не является потокобезопасной. Способы сделать его потокобезопасным включают синхронизацию при каждом доступе или очень осторожно , заставляя его использовать блокировку с двойной проверкой (которая безопасна для модели памяти Java 5, если вы все делаете правильно).
Обратите внимание, что из-за ленивой инициализации классов ваш последний код все равно будет создавать объект без необходимости, если вы вызываете статические методы в классе, не желая создавать экземпляр.
Существует шаблон, использующий вложенный класс для выполнения инициализация, которая может сделать этот процесс более ленивым, но лично вторая форма почти всегда подходит для меня сама по себе.
В Effective Java есть более подробная информация об этом, но у меня нет ее, чтобы найти номер элемента , Я
Я думаю, ваша проблема в том, что вы смешиваете синглтон и ленивую инициализацию. Синглтон может быть реализован с помощью различных стратегий инициализации :
Все эти подходы обсуждаются в Эффективная Java 2-й пункт 71: разумно используйте отложенную инициализацию .
Я согласен с Anon, и в случае, когда я всегда хочу создать экземпляр синглтона, я бы использовал
public class SingletonObject
{
public static SingletonObject REF = new SingletonObject();
private SingletonObject()
{
// no code req'd
}
}
Во второй форме ваш синглтон с нетерпением загружен, и это на самом деле предпочтительная форма (и первая не является поточно-ориентированной, как вы сами упомянули) . Активная загрузка - неплохая вещь для производственного кода, но есть контексты, в которых вы можете захотеть отложить загрузку ваших синглтонов, как обсуждал автор Guice, Боб Ли, в Ленивая загрузка синглтонов , который я цитирую ниже:
Во-первых, зачем вам отложенная загрузка синглтон? В производстве вы обычно хотят с нетерпением загрузить все ваши синглтоны, чтобы вы ловили ошибки рано и возьмите любую производительность спереди, но на тестах и во время разработка, вы хотите только загрузить что вам абсолютно необходимо, чтобы не терять время.
До Java 1.5 я лениво загружал синглтоны, использующие простой старый синхронизация, простая, но эффективная:
статический экземпляр Singleton; общедоступный статический синхронизированный синглтон getInstance () { если (экземпляр == нуль) экземпляр == новый синглтон (); возвратный экземпляр; }
Изменения в модели памяти в 1.5 включил печально известный Double-Checked Идиома блокировки (DCL). Чтобы реализовать DCL, вы проверяете поле
volatile
в общий путь и синхронизировать только тогда, когда необходимо:статический изменчивый экземпляр Singleton; public static Singleton getInstance () { if (instance == null) { синхронизированный (Singleton.class) { если (экземпляр == нуль) экземпляр == новый синглтон (); } } возвратный экземпляр; }
Но
volatile
не намного быстрее чемсинхронизировано
,синхронизировано
равно довольно быстро в настоящее время, а DCL требует больше кода, поэтому даже после выхода 1.5 Я продолжал использовать старый добрый синхронизация.Каково же мое удивление сегодня, когда Джереми Мэнсон указал мне на Инициализация по требованию держателя (IODH) идиома , которая требует очень маленький код и ноль накладные расходы на синхронизацию. Ноль, как в даже быстрее, чем
volatile
. ИОДГ требуется такое же количество строк код как обычная старая синхронизация, и это быстрее, чем DCL!IODH использует ленивый класс инициализация. JVM не будет работать статический инициализатор класса, пока вы действительно прикоснуться к чему-то в классе. Это относится к статическим вложенным классам, тоже. В следующем примере JLS гарантирует, что JVM не будет инициализировать
экземпляр
, пока кто-нибудь вызываетgetInstance ()
:статический класс SingletonHolder { статический экземпляр синглтона = новый синглтон (); } public static Singleton getInstance () { return SingletonHolder.instance; }
[...]
Обновление: Кредит, причитающийся, Действующая Java (авторское право 2001) подробно описал этот образец в пункте 48. Далее указывается, что вам все еще нужно использовать синхронизацию или DCL в нестатических контекстах.
Я также переключил обработку одиночных элементов в моя структура от синхронизации до DCL и увидел еще 10% производительности повышение (по сравнению с тем, как я начал используя быстрое отражение cglib). я только использовал один поток в моем микро-тесте, так что повышение параллелизма может быть даже больше, учитывая, что я заменил сильно оспариваемый замок с относительно мелкозернистое летучее поле доступ.
Обратите внимание, что Джошуа Блох теперь рекомендует (начиная с Effective Java, 2-е изд.) реализовывать синглтоны с использованием одноэлементного перечисления
, как указано Джоником .