Programing

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

c10106 2022. 4. 28. 19:52
반응형

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

나는 자바의 제네릭이 에 비해 다소 열등하다는 것을 안다.넷.

나는 일반 수업이 있다.Foo<T>그리고 나는 정말로 a를 인스턴스화해야 한다.TFoo매개 변수가 없는 생성자를 사용하는 경우.어떻게 하면 자바의 한계를 극복할 수 있을까?

한 가지 방법은 합격하는 것이다.Bar.class있든지 간에 - 것을 (또는 것은 유형이다 - 적절한 것은 지만하다)Class<T>참조) 및 해당 값을 필드로 유지:

public class Test {
    public static void main(String[] args) throws IllegalAccessException,
            InstantiationException {
        Generic<Bar> x = new Generic<>(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 Sket이 제안한 대로 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를 인스턴스화해야 한다.

간단한 답은 "당신은 그렇게 할 수 없다"는 것이다. 자바는 당신이 이것을 하는 것을 방해할 수반 생성물에 타입 삭제를 사용한다.

어떻게 하면 자바의 한계를 극복할 수 있을까?

한 가지 방법(다른 방법이 있을 수 있음)은 T의 인스턴스(instance)를 건설업자에게 넘겨줄 물체를 전달하는 것이다.Foo<T>아니면 방법이 있을 수도 있고setBar(T theInstanceofT);T를 받는 대신에 스스로 수업 시간에 즉흥적으로 하는 것이 아니라

Java 8의 경우 ....

https://stackoverflow.com/a/36315051/2648077 포스트에는 좋은 해결책이 있다.

이것은 Java 8을 사용한다.Supplier기능 인터페이스

The 사용Constructor.newInstance방법의Class.newInstance인스턴트화 예외에 대한 컴파일러 인식을 개선하기 위해 Java 9 이후 메서드가 더 이상 사용되지 않았다.

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

https://stackoverflow.com/a/2434094/848072에서.당신은 T 클래스에 대한 기본 생성자가 필요하다.

import java.lang.reflect.ParameterizedType;

class Foo<T> {
  
  public bar() {
    ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
    Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
    try {
      T t = (T) type.getDeclaredConstructor().newInstance();
      //Do whatever with t
    } catch (Exception e) {
      // Oops, no default constructor
      throw new RuntimeException(e);
    } 
  }
}

자바에서의 제네릭은 일반적으로 C#에서보다 더 강력하다.

객체를 구성하되 생성자/정적 방법을 하드웨딩하지 않으려면 추상 팩토리를 사용하십시오.당신은 모든 기본 디자인 패턴 책자, OOP 소개서 또는 인터웹에서 추상적 공장 패턴에 대한 자세한 정보와 튜토리얼을 찾을 수 있어야 한다.자바의 폐쇄 구문이 형편없다는 것 말고는 여기서 코드를 복제할 가치가 없다.

IIRC, C#는 일반적인 타입의 무아그 생성자를 명시하기 위한 특별한 경우를 가지고 있다.이러한 불규칙성은 정의상 클라이언트 코드가 이 특정한 형태의 구조를 사용하기를 원하며 돌연변이를 조장한다는 것을 전제로 한다.

이것에 대해 반성을 하는 것은 단지 잘못된 생각이다.Java의 제네릭은 컴파일 시간, 정적 타이핑 기능이다.런타임에 그것들을 사용하려는 시도는 무언가 잘못되고 있음을 분명히 보여주는 것이다.반사는 상세 코드, 런타임 오류, 확인되지 않은 종속성 및 보안 취약성을 야기한다.(Class.forName특히 사악하다.)

JUnit Test Setup에서 할 수 있어.

나는 겨울잠의 정면을 시험해 보고 싶어서 그것을 할 수 있는 일반적인 방법을 찾고 있었다.전면은 일반 인터페이스도 구현한다는 점에 유의하십시오.여기서 T는 데이터베이스 클래스, U는 기본 키 입니다. Ifacade<T,U>기본 키 U로 데이터베이스 객체 T에 접근하기 위한 전면이다.

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

나에게 효과가 있었던 빠른 해결책.나는 이미 이것에 대한 해답이 있다는 것을 알고 있고, 이것은 심지어 그것을 진행하기 위한 최선의 방법이 아닐지도 모른다.내 해결책도 Gson이 필요할 거야.

그러나, 나는 일반적인 종류의 클래스의 인스턴스를 만들어야 하는 상황에 부딪쳤다.java.lang.reflect.Type.

다음 코드는 null 인스턴스 변수를 사용하여 원하는 클래스의 인스턴스를 생성한다.

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

어디에myKnownType미리 알려지고 을 통해 얻는다.TypeToken.getType().

이제 이 개체에 적절한 속성을 설정할 수 있다.다시 말하지만, 이것이 이것을 하기 위한 최선의 방법은 아닐지 모르지만, 만약 당신이 필요로 하는 것이라면 그것은 빠른 해결책으로 효과가 있다.

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

반응형