Создать экземпляр универсального типа в Java?
414819 просмотра
27 ответа
Можно ли создать экземпляр универсального типа в Java? Я думаю, основываясь на том, что я видел, что ответ no
( из-за стирания типа ), но мне было бы интересно, если кто-нибудь увидит что-то, что мне не хватает:
class SomeContainer<E>
{
E createContents()
{
return what???
}
}
РЕДАКТИРОВАТЬ: Оказывается, что Super Type Token могут быть использованы для решения моей проблемы, но это требует много кода на основе отражения, как указано в некоторых ответах ниже.
Я оставлю это открытым на некоторое время, чтобы посмотреть, придумает ли кто-нибудь что-то кардинально отличное от статьи Artima Яна Робертсона .
Автор: David Citron Источник Размещён: 20.07.2019 05:03Ответы (27)
313 плюса
Ты прав. Вы не можете сделать new E()
. Но вы можете изменить его на
private static class SomeContainer<E> {
E createContents(Class<E> clazz) {
return clazz.newInstance();
}
}
Это боль. Но это работает. Заворачивание в фабричный шаблон делает его немного более терпимым.
Автор: Justin Rudd Размещён: 16.09.2008 06:10124 плюса
Не знаю, поможет ли это, но когда вы создаете подкласс (включая анонимный) универсальный тип, информация о типе доступна через отражение. например,
public abstract class Foo<E> {
public E instance;
public Foo() throws Exception {
instance = ((Class)((ParameterizedType)this.getClass().
getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
...
}
}
Итак, когда вы подкласс Foo, вы получаете экземпляр Bar, например,
// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );
Но это много работы, и работает только для подклассов. Может быть удобно, хотя.
Автор: noah Размещён: 16.09.2008 06:1897 плюса
В Java 8 вы можете использовать Supplier
функциональный интерфейс, чтобы достичь этого довольно легко:
class SomeContainer<E> {
private Supplier<E> supplier;
SomeContainer(Supplier<E> supplier) {
this.supplier = supplier;
}
E createContents() {
return supplier.get();
}
}
Вы бы построили этот класс так:
SomeContainer<String> stringContainer = new SomeContainer<>(String::new);
Синтаксис String::new
в этой строке является ссылкой на конструктор .
Если ваш конструктор принимает аргументы, вы можете вместо этого использовать лямбда-выражение:
SomeContainer<BigInteger> bigIntegerContainer
= new SomeContainer<>(() -> new BigInteger(1));
Автор: Daniel Pryden
Размещён: 30.03.2016 04:51
87 плюса
Вам понадобится какая-то абстрактная фабрика того или иного рода, чтобы переложить ответственность на:
interface Factory<E> {
E create();
}
class SomeContainer<E> {
private final Factory<E> factory;
SomeContainer(Factory<E> factory) {
this.factory = factory;
}
E createContents() {
return factory.create();
}
}
Автор: Tom Hawtin - tackline
Размещён: 16.09.2008 06:36
23 плюса
package org.foo.com;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* Basically the same answer as noah's.
*/
public class Home<E>
{
@SuppressWarnings ("unchecked")
public Class<E> getTypeParameterClass()
{
Type type = getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) type;
return (Class<E>) paramType.getActualTypeArguments()[0];
}
private static class StringHome extends Home<String>
{
}
private static class StringBuilderHome extends Home<StringBuilder>
{
}
private static class StringBufferHome extends Home<StringBuffer>
{
}
/**
* This prints "String", "StringBuilder" and "StringBuffer"
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException
{
Object object0 = new StringHome().getTypeParameterClass().newInstance();
Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
System.out.println(object0.getClass().getSimpleName());
System.out.println(object1.getClass().getSimpleName());
System.out.println(object2.getClass().getSimpleName());
}
}
Автор: Lars Bohl
Размещён: 03.09.2010 12:41
20 плюса
Если вам нужен новый экземпляр аргумента типа внутри обобщенного класса, тогда заставьте ваших конструкторов требовать его класса ...
public final class Foo<T> {
private Class<T> typeArgumentClass;
public Foo(Class<T> typeArgumentClass) {
this.typeArgumentClass = typeArgumentClass;
}
public void doSomethingThatRequiresNewT() throws Exception {
T myNewT = typeArgumentClass.newInstance();
...
}
}
Использование:
Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);
Плюсы:
- Намного проще (и менее проблематично), чем подход Super Type Token (STT) Робертсона.
- Гораздо эффективнее, чем подход STT (который съест ваш мобильный телефон на завтрак).
Минусы:
- Не могу передать класс конструктору по умолчанию (именно поэтому Foo является финальным). Если вам действительно нужен конструктор по умолчанию, вы всегда можете добавить метод установки, но тогда вы должны помнить, чтобы позвонить ей позже.
- Возражение Робертсона ... Больше баров, чем негодяев (хотя указание класса аргумента типа еще раз точно не убьет вас). И вопреки утверждениям Робертсона это не нарушает принцип DRY в любом случае, потому что компилятор обеспечит корректность типа.
- Не совсем
Foo<L>
доказательство. Для начала ...newInstance()
вызовет воблер, если класс аргумента типа не имеет конструктора по умолчанию. Это относится ко всем известным решениям, хотя в любом случае. - Отсутствует общая инкапсуляция подхода STT. Хотя это не имеет большого значения (учитывая огромные издержки производительности STT).
19 плюса
Вы можете сделать это сейчас, и это не требует кучу кода отражения.
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Конечно, если вам нужно вызвать конструктор, который потребует некоторого отражения, но это очень хорошо задокументировано, этот трюк не так!
Вот JavaDoc для TypeToken .
Автор: user177800 Размещён: 08.08.2014 02:0312 плюса
Подумайте о более функциональном подходе: вместо создания некоторого E из ничего (что явно является запахом кода), передайте функцию, которая знает, как ее создать, т.е.
E createContents(Callable<E> makeone) {
return makeone.call(); // most simple case clearly not that useful
}
Автор: Ingo
Размещён: 16.04.2015 04:20
8 плюса
Из Обучающего курса Java - Ограничения по Обобщениям :
Невозможно создать экземпляры параметров типа
Вы не можете создать экземпляр параметра типа. Например, следующий код вызывает ошибку во время компиляции:
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}
В качестве обходного пути вы можете создать объект параметра типа с помощью отражения:
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
Вы можете вызвать метод добавления следующим образом:
List<String> ls = new ArrayList<>();
append(ls, String.class);
Автор: Sergiy Sokolenko
Размещён: 13.09.2012 01:12
6 плюса
Вот вариант, который я придумал, он может помочь:
public static class Container<E> {
private Class<E> clazz;
public Container(Class<E> clazz) {
this.clazz = clazz;
}
public E createContents() throws Exception {
return clazz.newInstance();
}
}
РЕДАКТИРОВАТЬ: В качестве альтернативы вы можете использовать этот конструктор (но это требует экземпляра E):
@SuppressWarnings("unchecked")
public Container(E instance) {
this.clazz = (Class<E>) instance.getClass();
}
Автор: Mike Stone
Размещён: 16.09.2008 06:14
6 плюса
Если вы не хотите вводить имя класса дважды во время создания экземпляра, как в:
new SomeContainer<SomeType>(SomeType.class);
Вы можете использовать фабричный метод:
<E> SomeContainer<E> createContainer(Class<E> class);
Как в:
public class Container<E> {
public static <E> Container<E> create(Class<E> c) {
return new Container<E>(c);
}
Class<E> c;
public Container(Class<E> c) {
super();
this.c = c;
}
public E createInstance()
throws InstantiationException,
IllegalAccessException {
return c.newInstance();
}
}
Автор: jb.
Размещён: 17.09.2008 08:19
5 плюса
К сожалению, Java не позволяет вам делать то, что вы хотите. Смотрите официальный обходной путь :
Вы не можете создать экземпляр параметра типа. Например, следующий код вызывает ошибку во время компиляции:
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}
В качестве обходного пути вы можете создать объект параметра типа с помощью отражения:
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
Вы можете вызвать метод добавления следующим образом:
List<String> ls = new ArrayList<>();
append(ls, String.class);
Автор: Neepsnikeep
Размещён: 10.02.2016 12:32
4 плюса
Когда вы работаете с E во время компиляции, вы на самом деле не заботитесь о фактическом универсальном типе «E» (либо используете отражение, либо работаете с базовым классом универсального типа), поэтому пусть подкласс предоставляет экземпляр E.
Abstract class SomeContainer<E>
{
abstract protected E createContents();
public doWork(){
E obj = createContents();
// Do the work with E
}
}
**BlackContainer extends** SomeContainer<Black>{
Black createContents() {
return new Black();
}
}
Автор: Ira
Размещён: 12.02.2014 01:31
3 плюса
Ты можешь использовать:
Class.forName(String).getConstructor(arguments types).newInstance(arguments)
Но вам нужно указать точное имя класса, включая пакеты, например. java.io.FileInputStream
, Я использовал это для создания парсера математических выражений.
2 плюса
Я думал, что смогу сделать это, но довольно разочарован: это не работает, но я думаю, что все же стоит поделиться.
Может быть, кто-то может исправить:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface SomeContainer<E> {
E createContents();
}
public class Main {
@SuppressWarnings("unchecked")
public static <E> SomeContainer<E> createSomeContainer() {
return (SomeContainer<E>) Proxy.newProxyInstance(Main.class.getClassLoader(),
new Class[]{ SomeContainer.class }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> returnType = method.getReturnType();
return returnType.newInstance();
}
});
}
public static void main(String[] args) {
SomeContainer<String> container = createSomeContainer();
[*] System.out.println("String created: [" +container.createContents()+"]");
}
}
Это производит:
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
at Main.main(Main.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Строка 26 - это та, что с [*]
.
Единственное жизнеспособное решение - это решение @JustinRudd
Автор: Luigi R. Viggiano Размещён: 03.01.2013 08:132 плюса
Улучшение ответа @ Ноа.
Причина изменения
a] Безопаснее, если используется более 1 универсального типа, если вы изменили порядок.
b) Сигнатура родового типа класса время от времени меняется, так что вы не будете удивлены необъяснимыми исключениями во время выполнения.
Надежный код
public abstract class Clazz<P extends Params, M extends Model> {
protected M model;
protected void createModel() {
Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
for (Type type : typeArguments) {
if ((type instanceof Class) && (Model.class.isAssignableFrom((Class) type))) {
try {
model = ((Class<M>) type).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
Или используйте один лайнер
Однострочный код
model = ((Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance();
Автор: Amio.io
Размещён: 07.11.2014 08:13
0 плюса
Как вы сказали, вы не можете сделать это из-за стирания типа. Вы можете сделать это, используя отражение, но это требует много кода и много обработки ошибок.
Автор: Adam Rosenfield Размещён: 16.09.2008 06:060 плюса
Если вы имеете в виду,
new E()
то это невозможно. И я бы добавил, что это не всегда правильно - как узнать, есть ли у E открытый конструктор no-args? Но вы всегда можете делегировать создание другому классу, который знает, как создать экземпляр - это может быть Class<E>
или ваш собственный код, подобный этому
interface Factory<E>{
E create();
}
class IntegerFactory implements Factory<Integer>{
private static int i = 0;
Integer create() {
return i++;
}
}
Автор: Pavel Feldman
Размещён: 16.09.2008 06:42
0 плюса
return (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
Автор: Rachid
Размещён: 22.03.2011 09:59
0 плюса
Вы можете добиться этого с помощью следующего фрагмента:
import java.lang.reflect.ParameterizedType;
public class SomeContainer<E> {
E createContents() throws InstantiationException, IllegalAccessException {
ParameterizedType genericSuperclass = (ParameterizedType)
getClass().getGenericSuperclass();
@SuppressWarnings("unchecked")
Class<E> clazz = (Class<E>)
genericSuperclass.getActualTypeArguments()[0];
return clazz.newInstance();
}
public static void main( String[] args ) throws Throwable {
SomeContainer< Long > scl = new SomeContainer<>();
Long l = scl.createContents();
System.out.println( l );
}
}
Автор: bogdan
Размещён: 06.04.2012 11:26
0 плюса
Существуют различные библиотеки, которые можно решить E
, используя методы, аналогичные тем, которые обсуждались в статье Робертсона. Вот реализация, createContents
которая использует TypeTools для разрешения необработанного класса, представленного E:
E createContents() throws Exception {
return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}
Это предполагает, что getClass () преобразуется в подкласс SomeContainer и в противном случае завершится ошибкой, поскольку фактическое параметризованное значение E будет удалено во время выполнения, если оно не будет записано в подкласс.
Автор: Jonathan Размещён: 11.06.2014 11:420 плюса
Вот реализация, createContents
которая использует TypeTools для разрешения необработанного класса, представленного E
:
E createContents() throws Exception {
return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}
Этот подход работает, только если SomeContainer
он разделен на подклассы, поэтому фактическое значение E
фиксируется в определении типа:
class SomeStringContainer extends SomeContainer<String>
В противном случае значение E стирается во время выполнения и не подлежит восстановлению.
Автор: Jonathan Размещён: 04.12.2014 08:090 плюса
Надеюсь, что еще не поздно помочь!
Java - это безопасность типов, только Object может создать экземпляр.
В моем случае я не могу передать параметры в createContents
метод. Мое решение заключается в использовании расширений вместо всех приведенных ниже ответов.
private static class SomeContainer<E extends Object> {
E e;
E createContents() throws Exception{
return (E) e.getClass().getDeclaredConstructor().newInstance();
}
}
Это мой пример, в котором я не могу передать параметры.
public class SomeContainer<E extends Object> {
E object;
void resetObject throws Exception{
object = (E) object.getClass().getDeclaredConstructor().newInstance();
}
}
Использование отражения создает ошибку времени выполнения, если вы расширяете свой универсальный класс ни одним типом объекта. Для расширения вашего универсального типа на объект преобразуйте эту ошибку во время компиляции.
Автор: Se Song Размещён: 16.01.2019 09:070 плюса
что вы можете сделать, это -
Сначала объявите переменную этого универсального класса
2. Затем сделайте его конструктор и создайте экземпляр этого объекта.
Затем используйте его там, где вы хотите его использовать
пример-
1
private Class<E> entity;
2
public E getEntity(Class<E> entity) throws InstantiationException, IllegalAccessException {
return entity.newInstance();
}
3
E e = getEntity(entity);
Автор: Sudhanshu Jain
Размещён: 19.07.2019 12:11
0 плюса
Используйте класс TypeToken. Например:
public class MyClass<T>{
public T doSomething(){
return (T) new TypeToken<T>(){}.getRawType().newInstance();
}
}
Автор: cacheoff
Размещён: 20.07.2019 02:03
-1 плюса
Вы можете с загрузчиком классов и именем класса, в конечном итоге некоторые параметры.
final ClassLoader classLoader = ...
final Class<?> aClass = classLoader.loadClass("java.lang.Integer");
final Constructor<?> constructor = aClass.getConstructor(int.class);
final Object o = constructor.newInstance(123);
System.out.println("o = " + o);
Автор: Roald
Размещён: 04.02.2014 01:00
-1 плюса
Вот улучшенное решение, основанное на ParameterizedType.getActualTypeArguments
уже упомянутых @noah, @Lars Bohl и некоторых других.
Первое небольшое улучшение в реализации. Фабрика должна возвращать не экземпляр, а тип. Как только вы возвращаете экземпляр, Class.newInstance()
вы уменьшаете область использования. Потому что только конструкторы без аргументов могут быть вызваны следующим образом. Лучший способ - вернуть тип и позволить клиенту выбрать, какой конструктор он хочет вызвать:
public class TypeReference<T> {
public Class<T> type(){
try {
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){
throw new IllegalStateException("Could not define type");
}
if (pt.getActualTypeArguments().length != 1){
throw new IllegalStateException("More than one type has been found");
}
Type type = pt.getActualTypeArguments()[0];
String typeAsString = type.getTypeName();
return (Class<T>) Class.forName(typeAsString);
} catch (Exception e){
throw new IllegalStateException("Could not identify type", e);
}
}
}
Вот примеры использования. @Lars Bohl показал только лучший способ получить обобщенный род посредством расширения. @noah только через создание экземпляра с {}
. Вот тесты, чтобы продемонстрировать оба случая:
import java.lang.reflect.Constructor;
public class TypeReferenceTest {
private static final String NAME = "Peter";
private static class Person{
final String name;
Person(String name) {
this.name = name;
}
}
@Test
public void erased() {
TypeReference<Person> p = new TypeReference<>();
Assert.assertNotNull(p);
try {
p.type();
Assert.fail();
} catch (Exception e){
Assert.assertEquals("Could not identify type", e.getMessage());
}
}
@Test
public void reified() throws Exception {
TypeReference<Person> p = new TypeReference<Person>(){};
Assert.assertNotNull(p);
Assert.assertEquals(Person.class.getName(), p.type().getName());
Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
Assert.assertNotNull(ctor);
Person person = (Person) ctor.newInstance(NAME);
Assert.assertEquals(NAME, person.name);
}
static class TypeReferencePerson extends TypeReference<Person>{}
@Test
public void reifiedExtenension() throws Exception {
TypeReference<Person> p = new TypeReferencePerson();
Assert.assertNotNull(p);
Assert.assertEquals(Person.class.getName(), p.type().getName());
Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
Assert.assertNotNull(ctor);
Person person = (Person) ctor.newInstance(NAME);
Assert.assertEquals(NAME, person.name);
}
}
Примечание: вы можете заставить клиент TypeReference
всегда использовать {}
при создании экземпляра, сделав этот класс аннотацию: public abstract class TypeReference<T>
. Я не сделал этого, только чтобы показать стертый тестовый пример.
Вопросы из категории :
- java В чем разница между int и Integer в Java и C #?
- java Как я могу определить IP моего маршрутизатора / шлюза в Java?
- java Каков наилучший способ проверки XML-файла по сравнению с XSD-файлом?
- java Как округлить результат целочисленного деления?
- java Преобразование списка <Integer> в список <String>
- java Почему я не могу объявить статические методы в интерфейсе?
- java Библиотека Java SWIFT
- java Выключение компьютера
- java Как я могу воспроизвести звук на Java?
- java Когда выбирать отмеченные и непроверенные исключения
- generics Почему в C # нельзя хранить объект List <string> в переменной List <object>
- generics Преобразование общего типа из строки
- generics Лучший способ проверить, является ли универсальный тип строкой? (С #)
- generics Есть ли ограничение, которое ограничивает мой общий метод численными типами?
- generics Каковы различия между «универсальными» типами в C ++ и Java?
- generics Приведение списка <int> в список <string> в .NET 2.0
- generics Удалить дубликаты из списка <T> в C #
- generics Console.WriteLine и общий список
- generics Создать экземпляр универсального типа в Java?
- generics Что хорошего в дженериках, зачем их использовать?