[java] Java에서 제네릭 형식의 인스턴스를 만드시겠습니까?

Java에서 제네릭 형식의 인스턴스를 만들 수 있습니까? 나는 대답은 것을 알 한 내용을 기반으로 생각하고 no( 때문에 형의 삭제에 ), 그러나 누군가가 내가 부족 것을 볼 수 있을지에 관심이있을 것입니다 :

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

편집 : 슈퍼 타입 토큰 을 사용하여 내 문제를 해결할 수 있지만 아래 답변 중 일부에서 알 수 있듯이 많은 리플렉션 기반 코드가 필요합니다.

이안 로버트슨의 Artima Article 과는 전혀 다른 것이 있는지 알아보기 위해 잠시 동안 열어 두겠습니다 .



답변

당신이 올바른지. 당신은 할 수 없습니다 new E(). 하지만 당신은 그것을 변경할 수 있습니다

private static class SomeContainer<E> {
    E createContents(Class<E> clazz) {
        return clazz.newInstance();
    }
}

고통입니다. 그러나 작동합니다. 공장 패턴으로 포장하면 조금 더 견딜 수 있습니다.


답변

이것이 도움이된다면 Dunno이지만, 익명을 포함하여 일반 유형을 서브 클래스 화하면 유형 정보를 리플렉션을 통해 사용할 수 있습니다. 예를 들어

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

그러나 그것은 많은 작업이며 서브 클래스에서만 작동합니다. 그래도 편리 할 수 ​​있습니다.


답변

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


답변

벅을 전달하려면 일종의 추상 팩토리가 필요합니다.

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


답변

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

}


답변

제네릭 클래스 내에 유형 인수의 새 인스턴스가 필요한 경우 생성자가 클래스를 요구하게하십시오 …

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

장점 :

  • Robertson의 STT (Super Type Token) 방식보다 훨씬 간단하고 문제가 적습니다.
  • STT 방식보다 훨씬 효율적입니다 (아침 식사로 핸드폰을 먹을 것입니다).

단점 :

  • 클래스를 기본 생성자로 전달할 수 없습니다 (Foo가 최종적인 이유). 기본 생성자가 실제로 필요한 경우 항상 setter 메소드를 추가 할 수 있지만 나중에 호출해야합니다.
  • 로버트슨의 이의 제기 … 검은 양보다 막대가 더 많다. 그리고 Robertson의 주장과 달리 컴파일러가 형식 정확성을 보장하기 때문에 DRY 주체를 위반하지 않습니다.
  • 전적으로 Foo<L>증거가 아닙니다 . 우선 newInstance()타입 인수 클래스에 기본 생성자가 없으면 워 블러가 발생합니다. 이것은 어쨌든 모든 알려진 솔루션에 적용됩니다.
  • STT 접근법의 전체 캡슐화가 부족합니다. 그러나 STT의 엄청난 성능 오버 헤드를 고려하면 큰 문제는 아닙니다.

답변

지금이 작업을 수행 할 수 있으며 많은 반사 코드가 필요하지 않습니다.

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

물론 약간의 반영이 필요한 생성자를 호출해야하지만 매우 잘 문서화되어 있다면이 트릭은 그렇지 않습니다!

다음은 TypeToken 용 JavaDoc입니다 .