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


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

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




나는 나를 위해 일하는 빠르고 쉬운 방법을 발견했다. 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 클래스에있는 다른 메서드




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

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



이 코드를 살펴보십시오.

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

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




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 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 이전 버전에서도이를 수행 할 수 있지만 더 복잡합니다.




이 시도.

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



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

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




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

E[] a;

a = newArray(size);

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



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

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

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



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

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

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

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



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

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

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

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




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

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

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




Links