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가 호출 ObjectClass객체가 나타내는 클래스로 전달 된 인수가 반환 됩니다. 정적 메소드 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.