IT

Java에서 일반 클래스 인스턴스화

lottoking 2020. 6. 16. 07:57
반응형

Java에서 일반 클래스 인스턴스화


Java의 제네릭이 .Net보다 약간 열등하다는 것을 알고 있습니다.

나는 일반 클래스를 가지고 있으며 매개 변수가없는 생성자 사용하여 in Foo<T>을 인스턴스화해야합니다 . Java의 한계를 어떻게 해결할 수 있습니까?TFoo


한 가지 옵션은 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");
    }
}

또 다른 옵션은 "팩토리"인터페이스를 갖는 것이며 팩토리를 일반 클래스의 생성자에 전달합니다. 더 유연하며 리플렉션 예외에 대해 걱정할 필요가 없습니다.


Jon Skeet이 제안한 것처럼 이것은 Factory 구현입니다 .

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's 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...");
    }
}

매개 변수가없는 생성자를 사용하여 Foo에서 T를 인스턴스화해야합니다.

간단한 대답은 "그렇게 할 수 없다"는 것입니다. java는 타입 삭제를 사용하여 제네릭을 구현합니다.

Java의 한계를 어떻게 해결할 수 있습니까?

한 가지 방법 (다른 방법이있을 수 있음)은 T의 인스턴스를 전달할 객체를의 생성자에 전달하는 것입니다 Foo<T>. 또는 setBar(T theInstanceofT);클래스 자체에서 인스턴스화하는 대신 T를 얻는 방법이있을 수 있습니다 .


Java의 제네릭은 일반적으로 C #보다 강력합니다.

If you want to construct an object but without hardwiring a constructor/static method, use an abstract factory. You should be able to find detailed information and tutorials on the Abstract Factory Pattern in any basic design patterns book, introduction to OOP or all over the interwebs. It's not worth duplicating code here, other than to mention that Java's closure syntax sucks.

IIRC, C# has a special case for specifying a generic type has a no-args constructor. This irregularity, by definition, presupposes that client code wants to use this particular form of construction and encourages mutability.

Using reflection for this is just wrongheaded. Generics in Java are a compile-time, static-typing feature. Attempts to use them at runtime are a clear indication of something going wrong. Reflection causes verbose code, runtime failures, unchecked dependencies and security vulnerabilities. (Class.forName is particularly evil.)


From https://stackoverflow.com/a/2434094/848072. You need a default constructor for T class.


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);
    } 
  }
}


I could do this in a JUnit Test Setup.

I wanted to test a Hibernate facade so I was looking for a generic way to do it. Note that the facade also implements a generic interface. Here T is the database class and U the primary key. Ifacade<T,U> is a facade to access the database object T with the primary key 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();
}

Quick solution that worked for me. I see there is already an answer for this and this may not even be the best way to go about it. Also, for my solution you'll need Gson.

However, I ran into a situation where I needed to create an instance of a generic class of type java.lang.reflect.Type.

The following code will create an instance of the class you want with null instance variables.

T object = new Gson().fromJson("{}", myKnownType);

Where myKnownType is known before hand and obtained via TypeToken.getType().

You can now set appropriate properties on this object. Again, this may not be the best way to do this but it works as a quick solution if that's what you need.


For Java 8 ....

There is a good solution at https://stackoverflow.com/a/36315051/2648077 post.

This uses Java 8 Supplier functional interface


Use The Constructor.newInstance method. The Class.newInstance method has been deprecated since Java 9 to enhance compiler recognition of instantiation exceptions.

public class Foo<T> {   
    public Foo()
    {
        Class<T> newT = null; 
        instantiateNew(newT);
    }

    T instantiateNew(Class<?> clsT)
    {
        T newT;
        try {
            newT = (T) clsT.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
            | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
        return newT;
    }
}

참고URL : https://stackoverflow.com/questions/1090458/instantiating-a-generic-class-in-java

반응형