Можно ли создать экземпляр универсального типа в Java? Я думаю, основываясь на том, что я видел, что ответ no
( из-за стирания типа ), но мне было бы интересно, если кто-нибудь увидит что-то, что мне не хватает:
class SomeContainer<E>
{
E createContents()
{
return what???
}
}
РЕДАКТИРОВАТЬ: Оказывается, супер токены типа могут быть использованы для решения моей проблемы, но это требует много кода на основе отражения, как указано в некоторых ответах ниже.
Я оставлю это открытым на некоторое время, чтобы посмотреть, придумает ли кто-нибудь что-то кардинально отличное от статьи Artima Яна Робертсона .
Ответы:
Ты прав. Вы не можете сделать
new E()
. Но вы можете изменить его наЭто боль. Но это работает. Заворачивание в фабричный шаблон делает его более терпимым.
источник
Class<?>
ссылки с использованием Guava и TypeToken, посмотрите этот ответ для кода и ссылок!Не знаю, поможет ли это, но когда вы создаете подкласс (включая анонимный) универсальный тип, информация о типе доступна через отражение. например,
Итак, когда вы подкласс Foo, вы получаете экземпляр Bar, например,
Но это много работы, и работает только для подклассов. Может быть удобно, хотя.
источник
Foo
не абстрактный. Но почему он работает только на анонимных подклассах Foo? Предположим, что мы делаемFoo
конкретный (мы исключаемabstract
), почему этоnew Foo<Bar>();
приведет к ошибке, аnew Foo<Bar>(){};
нет? (Исключение: «Класс не может быть приведен к параметризованному типу»)<E>
вclass Foo<E>
не связан с каким - либо конкретным типа. Вы увидите исключительное поведение , когдаE
не статически связан, как в:new Foo<Bar>()
,new Foo<T>() {...}
, илиclass Fizz <E> extends Foo<E>
. Первый случай не является статически связанным, он стирается во время компиляции. Второй случай заменяет другую переменную типа (T) вместо,E
но все еще не связана. И в последнем случае должно быть очевидно, чтоE
все еще не связан.class Fizz extends Foo<Bar>
- в этом случае пользователиFizz
получают что-то, что является a,Foo<Bar>
и не может быть ничем, кроме aFoo<Bar>
. Поэтому в этом случае компилятор с радостью закодирует эту информацию в метаданные классаFizz
и сделает ее доступной в видеParameterizedType
кода для отражения. Когда вы создаете анонимный внутренний класс,new Foo<Bar>() {...}
он делает то же самое, за исключением того, что вместоFizz
компилятора генерируется «анонимное» имя класса, которое вы не узнаете, пока внешний класс не будет скомпилирован.Foo<Bar<Baz>>
. Вы будете создавать экземпляр,ParameterizedTypeImpl
который не может быть создан явно. Таким образом, это хорошая идея, чтобы проверить,getActualTypeArguments()[0]
возвращает лиParameterizedType
. Если это так, то вы хотите получить необработанный тип и создать его экземпляр.В Java 8 вы можете использовать
Supplier
функциональный интерфейс, чтобы достичь этого довольно легко:Вы бы построили этот класс так:
Синтаксис
String::new
в этой строке является ссылкой на конструктор .Если ваш конструктор принимает аргументы, вы можете вместо этого использовать лямбда-выражение:
источник
SomeContainer stringContainer = new SomeContainer(String::new);
?Вам понадобится какая-то абстрактная фабрика того или иного рода, чтобы переложить ответственность на:
источник
Factory<>
- это интерфейс, поэтому здесь нет тела. Дело в том, что вам нужен слой косвенных указаний, чтобы передать методы методам, которые «знают» необходимый код для создания экземпляра. Гораздо лучше делать это с помощью нормального кода, а не металингвистическогоClass
илиConstructor
потому, что рефлексия приносит целый мир боли.SomeContainer<SomeElement> cont = new SomeContainer<>(SomeElement::new);
источник
class GenericHome<T> extends Home<T>{}
Если вам нужен новый экземпляр аргумента типа внутри обобщенного класса, тогда заставьте ваших конструкторов требовать его класса ...
Применение:
Плюсы:
Минусы:
Foo<L>
доказательство. Для начала ...newInstance()
вызовет воблер, если класс аргумента типа не имеет конструктора по умолчанию. Это относится ко всем известным решениям, хотя в любом случае.источник
Вы можете сделать это сейчас, и это не требует кучу кода отражения.
Конечно, если вам нужно вызвать конструктор, который потребует некоторого отражения, но это очень хорошо задокументировано, этот трюк не так!
Вот JavaDoc для TypeToken .
источник
Подумайте о более функциональном подходе: вместо создания некоторого E из ничего (что явно является запахом кода), передайте функцию, которая знает, как ее создать, т.е.
источник
Exception
использоватьSupplier<E>
вместо этого.Из Обучающего курса Java - Ограничения по Обобщениям :
Невозможно создать экземпляры параметров типа
Вы не можете создать экземпляр параметра типа. Например, следующий код вызывает ошибку во время компиляции:
В качестве обходного пути вы можете создать объект параметра типа с помощью отражения:
Вы можете вызвать метод добавления следующим образом:
источник
Вот вариант, который я придумал, он может помочь:
РЕДАКТИРОВАТЬ: В качестве альтернативы вы можете использовать этот конструктор (но это требует экземпляра E):
источник
Если вы не хотите вводить имя класса дважды во время создания экземпляра, как в:
Вы можете использовать фабричный метод:
Как в:
источник
К сожалению, Java не позволяет вам делать то, что вы хотите. Смотрите официальный обходной путь :
источник
Когда вы работаете с E во время компиляции, вы на самом деле не заботитесь о фактическом универсальном типе «E» (либо используете отражение, либо работаете с базовым классом универсального типа), поэтому пусть подкласс предоставляет экземпляр E.
источник
Ты можешь использовать:
Но вам нужно указать точное имя класса, включая пакеты, например.
java.io.FileInputStream
, Я использовал это для создания парсера математических выражений.источник
foo.getClass().getName()
. Откуда этот экземпляр? В настоящее время я передаю один в конструктор в проекте, над которым я сейчас работаю.Надеюсь, что еще не поздно помочь!
Java - это безопасность типов, только Object может создать экземпляр.
В моем случае я не могу передать параметры в
createContents
метод. Мое решение заключается в использовании расширений вместо всех приведенных ниже ответов.Это мой пример, в котором я не могу передать параметры.
Использование отражения создает ошибку времени выполнения, если вы расширяете свой универсальный класс ни одним типом объекта. Для расширения вашего универсального типа на объект преобразуйте эту ошибку во время компиляции.
источник
Используйте
TypeToken<T>
класс:источник
(T) new TypeToken<T>(getClass()){}.getRawType().newInstance();
Я думал, что смогу сделать это, но довольно разочарован: это не работает, но я думаю, что все же стоит поделиться.
Может быть, кто-то может исправить:
Это производит:
Строка 26 это та, что с
[*]
.Единственное жизнеспособное решение - это решение @JustinRudd
источник
Улучшение ответа @ Ноа.
Причина изменения
а] Безопаснее, если используется более 1 универсального типа, если вы изменили порядок.
б] Сигнатура родового типа класса время от времени меняется, так что вы не будете удивлены необъяснимыми исключениями во время выполнения.
Надежный код
Или используйте один лайнер
Однострочный код
источник
что вы можете сделать, это -
Сначала объявите переменную этого универсального класса
2. Затем сделайте его конструктор и создайте экземпляр этого объекта.
Затем используйте его там, где вы хотите его использовать
пример-
1
2
3.
источник
Как вы сказали, вы не можете сделать это из-за стирания типа. Вы можете сделать это с помощью отражения, но это требует много кода и много обработки ошибок.
источник
Если вы имеете в виду,
new E()
то это невозможно. И я бы добавил, что это не всегда правильно - как узнать, есть ли у E открытый конструктор no-args? Но вы всегда можете делегировать создание другому классу, который знает, как создать экземпляр - это может бытьClass<E>
или ваш собственный код, подобный этомуисточник
источник
SomeContainer
это простоObject
. Следовательно,this.getClass().getGenericSuperclass()
возвращаетClass
(класс java.lang.Object), а не aParameterizedType
. На самом деле это уже было указано коллегиальным ответом stackoverflow.com/questions/75175/… .Вы можете достичь этого с помощью следующего фрагмента:
источник
Существуют различные библиотеки, которые можно решить
E
, используя методы, аналогичные тем, которые обсуждались в статье Робертсона. Вот реализация,createContents
которая использует TypeTools для разрешения необработанного класса, представленного E:Это предполагает, что getClass () преобразуется в подкласс SomeContainer и в противном случае завершится ошибкой, поскольку фактическое параметризованное значение E будет удалено во время выполнения, если оно не будет записано в подкласс.
источник
Вот реализация,
createContents
которая использует TypeTools для разрешения необработанного класса, представленногоE
:Этот подход работает, только если
SomeContainer
он разделен на подклассы, поэтому фактическое значениеE
фиксируется в определении типа:В противном случае значение E стирается во время выполнения и не подлежит восстановлению.
источник
Вы можете с загрузчиком классов и именем класса, в конечном итоге некоторые параметры.
источник
Вот улучшенное решение, основанное на
ParameterizedType.getActualTypeArguments
уже упомянутых @noah, @Lars Bohl и некоторых других.Первое небольшое улучшение в реализации. Фабрика должна возвращать не экземпляр, а тип. Как только вы возвращаете экземпляр,
Class.newInstance()
вы уменьшаете область использования. Потому что только конструкторы без аргументов могут быть вызваны следующим образом. Лучший способ - вернуть тип и позволить клиенту выбрать, какой конструктор он хочет вызвать:Вот примеры использования. @Lars Bohl показал только лучший способ получить обобщенный род посредством расширения. @noah только через создание экземпляра с
{}
. Вот тесты, чтобы продемонстрировать оба случая:Примечание: вы можете заставить клиент
TypeReference
всегда использовать{}
при создании экземпляра, сделав этот класс аннотации:public abstract class TypeReference<T>
. Я не сделал этого, только чтобы показать стертый тестовый пример.источник