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입니다 .