Я знаю, что дженерики Java несколько уступают .Net.
У меня есть общий класс Foo<T>
, и мне действительно нужно создать экземпляр T
в Foo
с помощью конструктора без параметров. Как можно обойти ограничение Java?
Я знаю, что дженерики Java несколько уступают .Net.
У меня есть общий класс Foo<T>
, и мне действительно нужно создать экземпляр T
в Foo
с помощью конструктора без параметров. Как можно обойти ограничение Java?
Один из вариантов состоит в том, чтобы передать Bar.class
(или любой другой тип, который вас интересует, любым способом указания соответствующей ссылки Class<T>
) и сохранить это значение как поле:
public class Test
{
public static void main(String [] args)
throws Exception // Just for simplicity!
{
Generic<Bar> x = new Generic<Bar>(Bar.class);
Bar y = x.buildOne();
}
}
public class Generic<T>
{
private Class<T> clazz;
public Generic(Class<T> clazz)
{
this.clazz = clazz;
}
public T buildOne() throws InstantiationException,
IllegalAccessException
{
return clazz.newInstance();
}
}
public class Bar
{
public Bar()
{
System.out.println("Constructing");
}
}
Другой вариант - иметь интерфейс "factory", и вы передаете factory конструктору универсального класса. Это более гибко, и вам не нужно беспокоиться об исключениях отражения.
И это реализация Factory, поскольку предложил Jon Skeet:
interface Factory<T> {
T factory();
}
class Araba {
//static inner class for Factory<T> implementation
public static class ArabaFactory implements Factory<Araba> {
public Araba factory() {
return new Araba();
}
}
public String toString() { return "Abubeee"; }
}
class Generic<T> {
private T var;
Generic(Factory<T> fact) {
System.out.println("Constructor with Factory<T> parameter");
var = fact.factory();
}
Generic(T var) {
System.out.println("Constructor with T parameter");
this.var = var;
}
T get() { return var; }
}
public class Main {
public static void main(String[] string) {
Generic<Araba> gen = new Generic<Araba>(new Araba.ArabaFactory());
System.out.print(gen.get());
}
}
Вывод:
Constructor with Factory<T> parameter
Abubeee
Вот довольно надуманный способ сделать это без явного использования аргумента конструктора. Вам необходимо расширить параметризованный абстрактный класс.
public class Test {
public static void main(String [] args) throws Exception {
Generic g = new Generic();
g.initParameter();
}
}
import java.lang.reflect.ParameterizedType;
public abstract class GenericAbstract<T extends Foo> {
protected T parameter;
@SuppressWarnings("unchecked")
void initParameter() throws Exception, ClassNotFoundException,
InstantiationException {
// Get the class name of this instance type.
ParameterizedType pt
= (ParameterizedType) getClass().getGenericSuperclass();
// You may need this split or not, use logging to check
String parameterClassName
= pt.getActualTypeArguments()[0].toString().split("\\s")[1];
// Instantiate the Parameter and initialize it.
parameter = (T) Class.forName(parameterClassName).newInstance();
}
}
public class Generic extends GenericAbstract<Foo> {
}
public class Foo {
public Foo() {
System.out.println("Foo constructor...");
}
}
Мне действительно нужно создать экземпляр T в Foo, используя параметр-less Конструктор
Простой ответ: "вы не можете этого сделать". java использует стирание типа для создания дженериков, что помешало бы вам сделать это.
Как можно обойти ограничение Java?
Один из способов (могут быть другие) - передать объект, который вы передадите экземпляру T в конструктор Foo<T>
. Или у вас может быть метод setBar(T theInstanceofT);
, чтобы получить ваш T вместо создания экземпляра в классе, который он сам.
Общие понятия в Java, как правило, более мощные, чем в С#.
Если вы хотите построить объект, но без аппаратного конструктора/статического метода, используйте абстрактный factory. Вы должны иметь возможность находить подробную информацию и учебные пособия по шаблону Abstract Factory в любой базовой книге шаблонов проектирования, введении в ООП или во всех средах. Это не стоит дублировать код здесь, кроме упоминания о том, что синтаксис закрытия Java отстой.
IIRC, С# имеет специальный случай для указания, что общий тип имеет конструктор no-args. Эта нерегулярность, по определению, предполагает, что клиентский код хочет использовать эту конкретную форму построения и поощряет изменчивость.
Использование отражения для этого просто неверно. Генераторы в Java - это функция статического ввода времени компиляции. Попытки использовать их во время выполнения - это четкое указание на то, что что-то происходит не так. Отражение вызывает подробный код, ошибки выполнения, необработанные зависимости и уязвимости безопасности. (Class.forName
особенно злой.)
От fooobar.com/questions/15894/.... Для класса T вам нужен конструктор по умолчанию.
import java.lang.reflect.ParameterizedType;
class Foo {
public bar() {
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class type = (Class) superClass.getActualTypeArguments()[0];
try {
T t = type.newInstance();
//Do whatever with t
} catch (Exception e) {
// Oops, no default constructor
throw new RuntimeException(e);
}
}
}
Я мог бы сделать это в JUnit Test Setup.
Я хотел протестировать фасад Hibernate, поэтому я искал общий способ сделать это. Обратите внимание, что фасад также реализует общий интерфейс. Здесь T - класс базы данных, а U - первичный ключ.
Ifacade<T,U>
- это фасад для доступа к объекту базы данных T с первичным ключом U.
public abstract class GenericJPAController<T, U, C extends IFacade<T,U>>
{
protected static EntityManagerFactory emf;
/* The properties definition is straightforward*/
protected T testObject;
protected C facadeManager;
@BeforeClass
public static void setUpClass() {
try {
emf = Persistence.createEntityManagerFactory("my entity manager factory");
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
}
@AfterClass
public static void tearDownClass() {
}
@Before
public void setUp() {
/* Get the class name*/
String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2].getTypeName();
/* Create the instance */
try {
facadeManager = (C) Class.forName(className).newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
Logger.getLogger(GenericJPAController.class.getName()).log(Level.SEVERE, null, ex);
}
createTestObject();
}
@After
public void tearDown() {
}
/**
* Test of testFindTEntities_0args method, of class
* GenericJPAController<T, U, C extends IFacade<T,U>>.
* @throws java.lang.ClassNotFoundException
* @throws java.lang.NoSuchMethodException
* @throws java.lang.InstantiationException
* @throws java.lang.IllegalAccessException
*/
@Test
public void testFindTEntities_0args() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {
/* Example of instance usage. Even intellisense (NetBeans) works here!*/
try {
List<T> lista = (List<T>) facadeManager.findAllEntities();
lista.stream().forEach((ct) -> {
System.out.println("Find all: " + stringReport());
});
} catch (Throwable ex) {
System.err.println("Failed to access object." + ex);
throw new ExceptionInInitializerError(ex);
}
}
/**
*
* @return
*/
public abstract String stringReport();
protected abstract T createTestObject();
protected abstract T editTestObject();
protected abstract U getTextObjectIndex();
}
Для Java 8....
В fooobar.com/questions/15408/... есть хорошее решение.
В нем используется функциональный интерфейс Java 8 Supplier
Быстрое решение, которое сработало для меня. Я вижу, что для этого уже есть ответ, и это может быть даже не лучшим способом. Кроме того, для моего решения вам понадобится Gson.
Однако я столкнулся с ситуацией, когда мне нужно было создать экземпляр универсального класса типа java.lang.reflect.Type
.
Следующий код создаст экземпляр класса с нулевыми переменными экземпляра.
T object = new Gson().fromJson("{}", myKnownType);
Где myKnownType
известно заранее и получается через TypeToken.getType()
.
Теперь вы можете установить соответствующие свойства для этого объекта. Опять же, это не лучший способ сделать это, но он работает как быстрое решение, если это вам нужно.