Java에서 일반 배열을 만드는 방법은 무엇입니까?


1090

Java 제네릭의 구현으로 인해 다음과 같은 코드를 가질 수 없습니다.

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

타입 안전을 유지하면서 이것을 어떻게 구현할 수 있습니까?

Java 포럼에서 다음과 같은 솔루션을 보았습니다.

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

그러나 나는 정말로 무슨 일이 일어나고 있지 않습니다.


14
당신이 정말로 필요가 여기에 배열을 사용할 수 있나요? 컬렉션을 사용하는 것은 어떻습니까?
— matt b

12
예. 또한이 문제에 대해 컬렉션이 더 우아하다고 생각합니다. 그러나 :(이 클래스 지정을 위해 그리고 그들은 필요
— tatsuhirosatou

3
왜 여기에 반영이 필요한지 이해하지 못합니다. Java 문법은 이상합니다. 새 java.util.HashMap <long, long> (10)이 유효하지 않습니다. new long [] [10]이 유효하지 않습니다. new long [10] []이 유효합니다. 그 물건은 자바 프로그램을 작성할 수있는 프로그램을 작성하는 것이 더 어렵습니다.
— 청동 남자

답변:


703

당신의 GenSet"체크 된"또는 "체크되지 않은"입니까? 그게 무슨 뜻이야?

  • 확인 : 강력한 타이핑 . GenSet포함하는 객체의 유형을 명시 적으로 알고 있습니다 (즉, 생성자가 Class<E>인수 와 함께 명시 적으로 호출되었으며 , 메소드가 유형이 아닌 인수를 전달하면 예외가 발생합니다 E. 참조) Collections.checkedCollection.

    이 경우 다음과 같이 작성해야합니다.

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
  • 선택하지 않은 경우 : 약한 입력 입니다. 인수로 전달 된 객체에 대해서는 실제로 유형 검사가 수행되지 않습니다.

    이 경우에는

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }

    배열의 구성 요소 유형은 type 매개 변수 의 삭제 여야합니다 .

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }

이 모든 것은 Java에서 알려진 고의적이며 제네릭의 약점에서 비롯됩니다. 삭제를 사용하여 구현되었으므로 "일반"클래스는 런타임에 어떤 유형 인수로 작성되었는지 알 수 없으므로 유형을 제공 할 수 없습니다. 명시 적 메커니즘 (유형 검사)이 구현되지 않는 한 안전.


7
성능 측면에서 가장 좋은 방법은 무엇입니까? 루프 내 에서이 배열의 요소를 자주 가져와야합니다. 컬렉션이 더 느릴 지 모르지만이 둘 중 가장 빠른 것은 무엇입니까?
— user1111929

3
그리고 제네릭 형식이 바인드 된 경우 백업 배열은 바운딩 형식이어야합니다.
— Mordechai

5
@AaronDigulla 지정이 아니라 로컬 변수의 초기화임을 명확히하기 위해. 표현식 / 설명에는 주석을 달 수 없습니다.
— kennytm

1
@Varkhan 클래스 구현 내에서 이러한 배열의 크기를 조정하는 방법이 있습니까? 예를 들어 ArrayList와 같이 오버플로 후 크기를 조정하려는 경우. Object[] EMPTY_ELEMENTDATA = {}스토리지에 대한 ArrayList의 구현을 찾았습니다 . 이 메커니즘을 사용하여 제네릭을 사용하는 유형을 몰라도 크기를 조정할 수 있습니까?
— JourneyMan

2
일반적인 타입으로 메소드를 만들고자하는 사람들을 위해 이것을 사용하십시오.public void <T> T[] newArray(Class<T> type, int length) { ... }
— Daniel Kvist

225

당신은 이것을 할 수 있습니다 :

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

이것은 효과적인 Java 에서 일반 컬렉션을 구현하는 제안 된 방법 중 하나입니다 . 항목 26 . 유형 오류가 없으며 배열을 반복적으로 캐스팅 할 필요가 없습니다. 그러나 이것은 잠재적으로 위험하므로주의해서 사용해야합니다. 주석에 자세히 설명되어 있듯이 이것은 Object[]현재 E[]유형으로 위장되어 ClassCastException안전하지 않은 경우 예기치 않은 오류나 s 가 발생할 수 있습니다 .

일반적으로이 동작은 캐스트 배열이 내부적으로 (예 : 데이터 구조를 백업하기 위해) 사용되고 클라이언트 코드에 반환되거나 노출되지 않는 한 안전합니다. 제네릭 형식의 배열을 다른 코드로 반환해야하는 경우 Array언급 한 리플렉션 클래스가 올바른 방법입니다.


가능한 List경우 제네릭을 사용하는 경우 배열보다는 s로 작업하는 것이 훨씬 행복 합니다. 확실히 선택의 여지가 없지만 컬렉션 프레임 워크를 사용하는 것이 훨씬 강력합니다.


47
String[] s=b;위의 test()방법 과 같이 배열이 유형이 지정된 배열로 취급되는 경우에는 작동하지 않습니다 . E의 배열이 실제로는 아니기 때문에 Object []입니다. 당신이 원하는 경우,이 문제는, 예를 들면 List<String>[]- 당신은 사용할 수 없습니다 Object[]그를 들어, 당신은 있어야합니다 List[]특히. 반영된 Class <?> 배열 생성을 사용해야하는 이유는 무엇입니까?
— Lawrence Dol

8
코너 케이스 / 문제는 예를 들어 로 입력 public E[] toArray() { return (E[])internalArray.clone(); }될 때 수행하려는 경우 internalArray이며 E[]실제로는 Object[]입니다. 유형 Object[]이 지정된 배열에 할당 할 수 없기 때문에 유형 캐스트 ​​예외로 인해 런타임에 실패 E합니다.
— Lawrence Dol

17
기본적 으로이 방법은 배열을 반환하거나 전달하지 않거나 특정 유형의 배열이 필요한 클래스 외부의 장소에 저장하지 않는 한 작동합니다. 강의실에있는 한 E는 지워지기 때문에 괜찮습니다. "위험한"상태입니다. 반품을 시도하면 안전하지 않다는 경고가 표시되지 않기 때문입니다. 그러나 조심하면 작동합니다.
— newacct

3
꽤 안전합니다. 에서 E[] b = (E[])new Object[1];당신이 명확하게 생성 된 배열에 대한 유일한 기준이라고 볼 수 b와 종류가 있다는 b것입니다 E[]. 따라서 실수로 다른 유형의 다른 변수를 통해 동일한 어레이에 액세스 할 위험이 없습니다. 대신에, 당신은 Object[] a = new Object[1]; E[]b = (E[])a; 당신이 사용하는 방법에 대해 편집증이 있어야합니다 a.
— Aaron McDaid

5
Java 1.6 이상에서는 "Object []에서 T []로 검사되지 않은 캐스트"
— Quantum7

61

다음은 제네릭을 사용하여 형식 안전을 유지하면서 찾고있는 형식의 배열을 얻는 방법입니다 (다른 답변과 달리 Object배열 을 반환 하거나 컴파일 타임에 경고를 표시 함).

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

당신이 볼 수있는 경고없이 컴파일하고, 같은 main당신의 인스턴스 선언 어떤 유형, GenSet등, 당신은 할당 할 수있는 a해당 유형의 배열에, 당신은에서 요소를 할당 할 수 a배열 즉, 해당 유형의 변수 배열의 값이 올바른 유형입니다.

Java Tutorials 에서 설명한 것처럼 클래스 리터럴을 런타임 유형 토큰으로 사용하여 작동합니다 . 클래스 리터럴은 컴파일러에서의 인스턴스로 처리됩니다 java.lang.Class. 하나를 사용하려면으로 클래스 이름을 따르십시오 .class. 따라서 클래스를 나타내는 객체 String.class로 작동 Class합니다 String. 또한 인터페이스, 열거 형, 모든 차원 배열 (예 :) String[].class, 프리미티브 (예 :) int.class및 키워드 void(예 :)에서도 작동합니다 void.class.

Class자체는 일반적입니다 (로 선언 Class<T>, 여기서 객체가 나타내는 T유형을 Class나타냄) . 의 유형은 String.class입니다 Class<String>.

따라서에 대한 생성자를 호출 할 때마다 인스턴스의 선언 된 유형의 GenSet배열을 나타내는 첫 번째 인수에 대해 클래스 리터럴을 전달 GenSet합니다 (예 : String[].classfor GenSet<String>). 프리미티브는 유형 변수에 사용할 수 없기 때문에 프리미티브 배열을 얻을 수 없습니다.

생성자 내에서 메서드를 호출하면 메서드 cast가 호출 Object된 Class객체가 나타내는 클래스로 전달 된 인수가 반환 됩니다. 정적 메소드 newInstance를 호출하면 첫 번째 인수로 전달 된 객체와 두 번째 인수로 전달 된 길이로 표시되는 유형의 배열이 java.lang.reflect.Array반환됩니다 . 메소드 호출 복귀 어레이의 구성 형태를 나타내는 객체에 의해 표현 (예를 들어 메소드가 호출되는 객체 에 대해 , 경우 생성 객체 배열을 나타내지 않음).ObjectClassintgetComponentTypeClassClassString.classString[].classnullClass

마지막 문장은 완전히 정확하지 않습니다. 호출 String[].class.getComponentType()반환 Class클래스를 나타내는 객체 String, 그러나 그것의 유형입니다 Class<?>하지, Class<String>당신은 다음과 같은 일을 할 수없는 이유이다.

String foo = String[].class.getComponentType().cast("bar"); // won't compile

객체 Class를 반환하는 모든 메소드에 동일하게 적용됩니다 Class.

이 답변에 대한 Joachim Sauer의 의견에 대해 (나는 스스로 의견을 제시 할만큼 평판이 충분하지 않습니다), 캐스트를 사용하는 예제 T[]는 컴파일러가 유형 안전을 보장 할 수 없기 때문에 경고를 표시합니다.


Ingo의 의견에 관한 편집 :

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

5
이것은 쓸모가 없으며, 새로운 String [...]을 작성하는 복잡한 방법 일뿐입니다. 그러나 실제로 필요한 것은 public static <T> T [] newArray (int size) {...}와 같습니다. 이것은 단순히 자바 느와르에는 ​​존재하지 않습니다. 반사와 함께 시뮬레이션 할 수 있습니다-그 이유는 인스턴스화되는 일반 유형은 런타임에 사용할 수 없습니다.
— Ingo

4
@Ingo 무슨 소리 야? 내 코드를 사용하여 모든 유형의 배열을 만들 수 있습니다.
— gdejohn

3
@Charlatan : 물론입니다. 새로운 것이 가능합니다.]. 문제는 유형과시기를 아는 사람입니다. 따라서 모든 것이 제네릭 형식이면 할 수 없습니다.
— Ingo

2
의심의 여지가 없습니다. 요점은, 제네릭 타입 X의 런타임에 Class 객체를 얻지 못한다는 것입니다.
— Ingo

2
거의. 나는 이것이 new []로 달성 할 수있는 것 이상임을 인정한다. 실제로 이것은 거의 항상 작업을 수행합니다. 그러나, 예를 들어, E [] toArray () 메소드를 가지며 실제로 진정한 E [] 배열을 리턴하는 E로 매개 변수화 된 컨테이너 클래스를 작성할 수는 없습니다. 컬렉션에 하나 이상의 E- 개체가있는 경우에만 코드를 적용 할 수 있습니다. 따라서 일반적인 해결책은 불가능합니다.
— Ingo

42

이것은 안전한 유형의 유일한 답변입니다

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

나는 그것을 찾아야 만했다. 그러나 그렇다. 두 번째 "길이"인수 Arrays#copyOf()는 첫 번째 인수로 제공된 배열의 길이와 무관하다. 이 호출의 비용을 지불 않지만 즉, 영리 Math#min()하고 System#arrayCopy(), 어느 쪽도 아니는이 작업을 수행하기가 반드시 필요하다. docs.oracle.com/javase/7/docs/api/java/util/…
— seh

8
E유형 변수 인 경우 작동하지 않습니다 . varargs는 Ewhen E가 type 변수 인 지움 배열을 생성하여와 크게 다르지 않습니다 (E[])new Object[n]. http://ideone.com/T8xF91을 참조하십시오 . 다른 답변보다 더 안전한 유형 은 아닙니다 .
— Radiodef

1
@Radiodef-솔루션은 컴파일 타임에 형식이 안전합니다. 삭제는 언어 사양의 일부가 아닙니다. 사양은 신중하게 작성되어 향후 완전히 수정 될 수 있으며이 솔루션은 다른 솔루션과 달리 런타임에서도 완벽하게 작동합니다.
— ZhongYu

@Radiodef-일반 배열 생성을 금지하는 것이 좋은 아이디어인지 여부는 논쟁의 여지가 있습니다. 어쨌든, 언어는 백도어를 떠납니다. vararg는 일반적인 배열 생성을 요구합니다. 언어가 허락 한 것처럼 좋다 new E[]. 귀하의 예에서 보여준 문제는이 질문과이 답변에 고유하지 않은 일반적인 삭제 문제입니다.
— ZhongYu

2
@Radiodef-약간의 차이가 있습니다. 이 솔루션의 정확성은 컴파일러에서 확인합니다. 그것은 강제적 인 캐스트의 인간 추론에 의존하지 않습니다. 이 특정 문제에 대한 차이는 중요하지 않습니다. 어떤 사람들은 조금 공상하기를 좋아합니다. 그게 전부입니다. OP의 말로 잘못 인도 된 사람이 있으면 의견과 내 의견으로 명확 해집니다.
— ZhongYu

33

더 많은 차원으로 확장하려면에 []및 차원 매개 변수를 추가하십시오 newInstance()( T유형 매개 변수, cls는이며 Class<T>, d1관통 d5하는 정수임).

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

자세한 내용 Array.newInstance()을 참조하십시오.


4
+1이 게시물의 속임수로 다차원 배열 생성에 대한 질문이 있었지만 구체적인 답변은 없습니다.
— Paul Bellora

1
@JordanC 아마도; 비록 그것이 stackoverflow.com/a/5671304/616460 과 동일하지만 ; 나는 내일을 처리하는 가장 좋은 방법에 대해 생각할 것입니다. 졸려요
— Jason C

14

Java 8에서는 람다 또는 메서드 참조를 사용하여 일종의 일반 배열을 만들 수 있습니다. 이것은 (을 전달하는 Class) 반사 방식과 유사 하지만 여기서는 반사를 사용하지 않습니다.

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

예를 들어 이것은에 의해 사용됩니다 <A> A[] Stream.toArray(IntFunction<A[]>).

이 수 익명 클래스를 사용하여 사전에 자바 (8)을 수행 할 수 있지만, 더 복잡합니다.


실제로 이와 같은 특별한 인터페이스가 필요하지 않습니다 ArraySupplier. 생성자를로 선언 GenSet(Supplier<E[]> supplier) { ...하고 같은 줄로 호출 할 수 있습니다.
— Lii

4
@Lii 내 예제와 동일하기 위해서는 IntFunction<E[]>이지만 맞습니다.
— Radiodef

11

이것은 효과적인 Java, 제 2 판 , 항목 25 의 5 장 (Generics)에서 다룹니다 . 배열 목록 선호

코드는 확인되지 않은 경고를 생성하지만 다음 주석으로 억제 할 수는 있지만 작동합니다.

@SuppressWarnings({"unchecked"})

그러나 Array 대신 List를 사용하는 것이 좋습니다.

OpenJDK 프로젝트 사이트 에서이 버그 / 기능에 대한 흥미로운 토론이 있습니다 .


8

Class 인수를 생성자에 전달할 필요는 없습니다. 이 시도.

public class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

과

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

결과:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

7

Java 제네릭은 컴파일 타임에 유형을 확인하고 적절한 캐스트를 삽입하지만 컴파일 된 파일에서 유형을 지우는 방식으로 작동합니다. 이로 인해 제네릭을 이해하지 못하는 (고의적 인 디자인 결정) 코드로 제네릭 라이브러리를 사용할 수 있지만 런타임시 유형이 무엇인지 일반적으로 알 수 없습니다.

공용 Stack(Class<T> clazz,int capacity)생성자는 런타임에 Class 객체를 전달 해야합니다. 즉, 런타임시 클래스 정보 를 사용하여 필요한 클래스 정보 를 사용할 수 있습니다. 그리고Class<T> 형식은 컴파일러가 전달한 Class 객체가 T 유형의 Class 객체인지 정확하게 확인한다는 의미입니다. T의 하위 클래스가 아니라 T의 수퍼 클래스가 아니라 정확하게 T입니다.

따라서 생성자에서 적절한 유형의 배열 객체를 만들 수 있습니다. 즉, 컬렉션에 저장 한 객체의 유형에 따라 컬렉션에 추가 된 시점에서 유형이 확인됩니다.


6

안녕하세요 스레드가 죽었지 만 이것에주의를 기울이고 싶습니다.

제네릭은 컴파일 시간 동안 유형 검사에 사용됩니다.

  • 따라서 목적은 들어오는 것이 필요한 것인지 확인하는 것입니다.
  • 당신이 돌려주는 것은 소비자에게 필요한 것입니다.
  • 이것을 확인하십시오 :

여기에 이미지 설명을 입력하십시오

제네릭 클래스를 작성할 때 타입 캐스팅 경고에 대해 걱정하지 마십시오. 사용할 때 걱정하십시오.


6

이 솔루션은 어떻습니까?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

작동하고 너무 단순 해 보이지 않습니다. 단점이 있습니까?


3
단정하지만 '수동으로'호출하는 경우에만 작동합니다. 즉, 요소를 개별적으로 전달합니다. 의 새 인스턴스를 만들 T[]수 없으면 프로그래밍 방식 T[] elems으로 함수에 전달할 수 없습니다 . 그리고 가능하다면 기능이 필요하지 않습니다.
— orlade

5

이 코드를보십시오 :

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

모든 종류의 객체 목록을 동일한 유형의 배열로 변환합니다.


예, 빈 배열이 아닌 null을 반환합니다. 최선을 다하지만 이상적이지는 않습니다.
— Kevin Cox

List예를 들어 toArray(Arrays.asList("abc", new Object()))throw 할 객체에 둘 이상의 유형의 객체가있는 경우에도 실패 할 수 있습니다 ArrayStoreException.
— Radiodef

나는 이것의 제거 된 버전을 사용했다; 내가 사용할 수있는 첫 번째 일은 인정했지만 더 관련이있는 솔루션을 시도하지는 않았습니다. for루프와 다른 것들을 피하기 Arrays.fill(res, obj);위해 각 인덱스에 대해 동일한 값을 원했기 때문에 사용 했습니다.
— bbarker 1

5

나는 나를 위해 빠르고 쉬운 방법을 찾았습니다. Java JDK 8에서만 이것을 사용했습니다. 이전 버전에서 작동하는지 모르겠습니다.

특정 형식 매개 변수의 제네릭 배열을 인스턴스화 할 수는 없지만 이미 생성 된 배열을 제네릭 클래스 생성자에 전달할 수 있습니다.

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

이제 메인에서 다음과 같이 배열을 만들 수 있습니다.

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

배열의 유연성을 높이기 위해 링크 된 목록을 사용할 수 있습니다. Java.util.ArrayList 클래스에있는 ArrayList 및 기타 메소드


4

예는 Java 리플렉션을 사용하여 배열을 만드는 것입니다. 형식 안전하지 않으므로 일반적으로 권장하지 않습니다. 대신 내부 List를 사용하고 배열을 피하십시오.


13
두 번째 예제 (Array.newInstance () 사용) 는 실제로 형식이 안전합니다. 이는 Class 객체의 T 유형이 배열의 T와 일치해야하기 때문에 가능합니다. 기본적으로 Java 런타임이 제네릭에 대해 버리는 정보를 제공하도록합니다.
— Joachim Sauer


3

이 코드 스 니펫을 작성하여 간단한 자동 테스트 유틸리티를 위해 전달되는 클래스를 반사적으로 인스턴스화했습니다.

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

이 세그먼트를 참고하십시오.

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

여기서 Array.newInstance (클래스의 클래스, 배열의 크기)를 시작하는 배열 . 클래스는 기본 (int.class) 및 객체 (Integer.class) 둘 다일 수 있습니다.

BeanUtils는 Spring의 일부입니다.


3

실제로 더 쉬운 방법은 객체 배열을 만들고 다음 예제와 같이 원하는 유형으로 캐스팅하는 것입니다.

T[] array = (T[])new Object[SIZE];

여기서 SIZE상수 T는 유형 식별자입니다.


1

다른 사람들이 제안한 강요된 캐스트는 저에게 효과적이지 않아 불법적 인 캐스트를 예외로합니다.

그러나이 암시 적 캐스트는 정상적으로 작동했습니다.

Item<K>[] array = new Item[SIZE];

여기서 Item은 멤버를 포함하여 정의한 클래스입니다.

private K value;

이 방법으로 K 유형의 배열 (항목에 값만있는 경우) 또는 항목 클래스에 정의하려는 일반 유형의 배열을 얻을 수 있습니다.


1

귀하가 게시 한 예에서 무슨 일이 일어나고 있는지에 대한 아무도 대답하지 않았습니다.

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

다른 사람들이 말했듯이 컴파일 중에 제네릭이 "지워졌습니다". 따라서 런타임에 제네릭 인스턴스는 구성 요소 유형이 무엇인지 알지 못합니다. 그 이유는 역사적으로, Sun은 기존 인터페이스 (소스 및 이진 모두)를 손상시키지 않고 제네릭을 추가하려고했습니다.

반면에 배열은 할 런타임에 자신의 구성 요소 유형을 알고있다.

이 예제는 생성자를 호출하는 코드 (유형을 아는)가 클래스에 필요한 유형을 알려주는 매개 변수를 전달함으로써 문제를 해결합니다.

따라서 응용 프로그램은 다음과 같은 클래스를 구성합니다.

Stack<foo> = new Stack<foo>(foo.class,50)

그리고 생성자는 이제 컴포넌트 타입이 무엇인지 (런타임에서) 알고 있으며 그 정보를 사용하여 리플렉션 API를 통해 배열을 구성 할 수 있습니다.

Array.newInstance(clazz, capacity);

마지막으로 컴파일러는 반환 된 배열 Array#newInstance()이 올바른 유형 임을 알 수있는 방법이 없기 때문에 유형 캐스트가 있습니다 (아직 알고 있지만).

이 스타일은 약간 추악하지만 때로는 어떤 이유로 든 런타임에 구성 요소 유형을 알아야하는 일반 형식을 만드는 방법 (배열 생성 또는 구성 요소 유형의 인스턴스 생성 등)에있어 가장 나쁜 해결책이 될 수 있습니다.


1

이 문제에 대한 일종의 해결 방법을 찾았습니다.

아래 줄은 일반 배열 생성 오류를 발생시킵니다

List<Person>[] personLists=new ArrayList<Person>()[10];

그러나 List<Person>별도의 클래스로 캡슐화 하면 작동합니다.

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

게터를 통해 PersonList 클래스의 사람들을 노출시킬 수 있습니다. 아래 줄 List<Person>은 모든 요소에 배열을 제공합니다 . 즉,의 배열입니다 List<Person>.

PersonList[] personLists=new PersonList[10];

내가 작업하고있는 일부 코드에서 이와 같은 것이 필요했으며 이것이 작동하도록하는 것입니다. 지금까지 아무런 문제가 없습니다.


0

Object 배열을 만들어 어디서나 E로 캐스트 할 수 있습니다. 예, 그렇게하는 것이 깨끗하지는 않지만 적어도 작동해야합니다.


"우리는 약간의 설명과 맥락을 제공하는 긴 답변을 찾고 있습니다. 단 한 번의 답변 만 제공하지 말고 답변이 이상적이고 인용에 적합한 이유를 설명하십시오. 설명이없는 답변은 제거 될 수 있습니다."
— gparyani

일반 클래스가 Comparable 인터페이스를 구현하려는 경우와 같은 경우에는 작동하지 않습니다.
— RamPrasadBismil

7 년 전에 오신 것을 환영합니다.
— Esko

1
일반 코드에서 제네릭이 아닌 호출자에게 배열을 반환하려고 시도하면 작동하지 않습니다. 헤드 스크 라이팅 클래스 캐스트 예외가있을 것입니다.
— plugwash

0

이 시도.

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}

코드를 실행할 수 없습니다. Element클래스는 어디 에서 왔습니까?

0

이것에 대한 쉬운 해결 방법은 주 클래스 내부에 두 번째 "홀더"클래스를 중첩시키고이를 사용하여 데이터를 보유하는 것입니다.

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

3
실제로 작동하지 않습니다. new Holder<Thing>[10]일반적인 배열 생성입니다.
— Radiodef

0

이 질문과 관련이 없지만 generic array creation사용하기 위해 " "오류가 발생하는 동안

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

나는 다음과 같은 작품을 발견했다 @SuppressWarnings({"unchecked"}).

 Tuple<Long, String>[] tupleArray = new Tuple[10];

예, 이것은 관련이 없지만 같은 문제 (지우기, 배열 공분산)에 기인합니다. 다음은 매개 변수화 된 유형의 배열 작성에 대한 게시물의 예입니다. stackoverflow.com/questions/9542076/…
— Paul Bellora

0

이 코드가 효과적인 제네릭 배열을 만들지 궁금합니다.

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

편집 : 아마도 필요한 크기가 알려져 있고 작은 경우 이러한 배열을 만드는 다른 방법은 필요한 수의 "null"을 zeroArray 명령에 공급하는 것입니까?

분명히 이것은 createArray 코드를 사용하는 것만 큼 다재다능하지는 않습니다.


아니요, 작동하지 않습니다. varargs는 T언제 Ttype 변수인지 를 삭제 zeroArray합니다 Object[]. 즉,를 반환합니다 . http://ideone.com/T8xF91을 참조하십시오 .
— Radiodef

0

캐스트를 사용할 수 있습니다.

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

이것을 제안하려면 실제로 그 한계를 설명해야합니다. a수업 외부에 노출시키지 마십시오 !
— Radiodef

0

실제로 제네릭 배열을 시작할 수 없다는 우회 할 수있는 매우 독특한 솔루션을 찾았습니다. 당신이해야 할 일은 다음과 같이 일반 변수 T를 취하는 클래스를 만드는 것입니다.

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

그런 다음 배열 클래스에서 다음과 같이 시작하십시오.

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

를 시작하면 new Generic Invoker[]체크되지 않은 상태로 문제가 발생하지만 실제로는 문제가되지 않습니다.

배열에서 얻으려면 다음과 같이 array [i] .variable을 호출해야합니다.

public T get(int index){
    return array[index].variable;
}

배열 크기 조정과 같은 나머지는 Arrays.copyOf ()를 사용하여 다음과 같이 수행 할 수 있습니다.

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

그리고 add 함수는 다음과 같이 추가 될 수 있습니다 :

public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}

1
문제는 T일부 매개 변수화 된 형식의 배열이 아닌 일반 형식 매개 변수 형식의 배열을 만드는 것 입니다.
— Sotirios Delimanolis 2016 년

그러나 동일한 작업을 완료하며 사용자 정의 컬렉션을 더 쉽게 사용할 수 있도록 수업을 진행할 필요가 없습니다.
— Crab Nebula

어떤 작업 ? 문자 그대로 다른 작업입니다. 매개 변수화 된 형식의 배열과 일반 형식 매개 변수의 배열입니다.
— Sotirios Delimanolis 2016 년

제네릭 형식에서 배열을 만들 수 있습니까? 원래 문제는 제 방법을 사용하여 사용자가 클래스를 푸시하거나 객체를 문자열로 캐스팅하는 것과 같은 검사되지 않은 오류를주지 않고도 수행 할 수있는 일반 유형을 사용하여 배열을 초기화하는 것이 었습니다. 냉기처럼, 나는 내가하는 일에 최고가 아니며 프로그래밍을 위해 학교에 가지 않았지만 인터넷상의 다른 어린이가 들려주는 것보다는 약간의 입력을받을 자격이 있다고 생각합니다.
— Crab Nebula

Sotiros에 동의합니다. 답을 생각하는 두 가지 방법이 있습니다. 다른 질문에 대한 답이거나 질문을 일반화하려는 시도입니다. 둘 다 잘못되었거나 도움이되지 않습니다. "일반 배열"클래스를 구현하는 방법에 대한 지침을 찾는 사람들은 질문 제목을 읽을 때 읽기를 중단하거나 중단합니다. 그리고 30 개의 답변이있는 Q를 발견하면 끝까지 스크롤하여 SO 신규 이민자로부터 무의미한 답변을 읽을 가능성이 거의 없습니다.
— Stephen C

0

vnportnoy에 따르면 구문

GenSet<Integer> intSet[] = new GenSet[3];

다음과 같이 채워질 null 참조 배열을 만듭니다.

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

안전한 타입입니다.


-1
private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}

항상 코드에 설명을 추가하고 원래 게시 된 질문을 해결하는 이유를 설명해야합니다.
— mjuarez 2016 년

-1

일반 배열 생성은 Java에서 허용되지 않지만 다음과 같이 할 수 있습니다

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.