Java로 범용 배열을 만드는 방법?


13 Answers

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

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

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

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

가능하다면 generics를 사용하고 있다면 배열보다는 List 작업하는 것이 훨씬 행복 할 것입니다. 물론 때로는 선택의 여지가 없지만 콜렉션 프레임 워크를 사용하는 것이 훨씬 강력합니다.

Question

Java generics의 구현으로 인해 다음과 같은 코드를 작성할 수 없습니다.

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

그러나 나는 정말로 무슨 일이 일어나는지 알지 못합니다.




이 코드를 살펴보십시오.

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

그것은 모든 종류의 객체 목록을 같은 유형의 배열로 변환합니다.




이 문제를 해결하기위한 일종의 작업을 발견했습니다.

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

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

getter를 통해 PersonList 클래스의 사용자를 노출 할 수 있습니다. 아래 줄은 모든 요소에 List<Person> 이있는 배열을 제공합니다. 즉 List<Person> 배열.

PersonList[] personLists=new PersonList[10];

내가 일하고있는 일부 코드에서 이와 같은 것이 필요했고, 이것이 작동하도록했던 것입니다. 지금까지 문제 없음.




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

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

이는 생성자에서 적절한 유형의 배열 객체를 만들 수 있음을 의미합니다. 즉, 컬렉션에 저장하는 객체의 유형은 컬렉션에 추가되는 시점에서 유형이 검사됩니다.




다른 사람들이 제안한 강제 탄식은 저에게 효과적이지 않아 불법적 인 캐스팅을 제외하고 던졌습니다.

그러나이 암시 적 캐스트는 잘 작동했습니다.

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

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

private K value;

이 방법을 사용하면 K 유형 (항목에만 값이있는 경우) 또는 Item 클래스에 정의하려는 모든 일반 유형의 배열을 가져올 수 있습니다.




나는 나를 위해 일하는 빠르고 쉬운 방법을 발견했다. Java JDK 8에서만 사용했음을 유의하십시오. 이전 버전에서 작동하는지 모르겠습니다.

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

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

    // ...

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

    // Do whatever with the array...
}

이제 main에서 우리는 다음과 같이 배열을 생성 할 수 있습니다 :

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

        // ...

    }
}

배열과의 융통성을 높이기 위해 예를 들어 연결된 목록을 사용할 수 있습니다. ArrayList 및 Java.util.ArrayList 클래스에있는 다른 메서드




이 시도.

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



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

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

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



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

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

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




이것은 유형 안전 한 유일한 대답입니다.

E[] a;

a = newArray(size);

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



어쩌면이 질문에 관련이 없지만 사용하는 동안 " generic array creation "오류가 발생하는 동안

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

@SuppressWarnings({"unchecked"}) 하여 다음 작품을 찾아서 (나를 위해 일했습니다. @SuppressWarnings({"unchecked"}) :

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



이 코드 스 니펫을 사용하여 간단한 자동화 테스트 유틸리티에 전달되는 클래스를 실제로 반영합니다.

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




일반적인 배열 생성은 자바에서는 허용되지 않지만 당신은 그것을 할 수 있습니다.

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}



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[]>) 의해 사용됩니다.

익명의 클래스를 사용하여 Java 8 이전 버전에서도이를 수행 할 수 있지만 더 복잡합니다.



Related