Java에서 범용 어레이를 작성하는 방법
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;
}
하지만 난 무슨 일인지 정말 모르겠어.
답례로 질문을 하나 해야겠네요:GenSet
'어느새'는요?게게무 슨슨?
확인: 강한 타이핑.
GenSet
그 으로 인식하고 있습니다( 그 컨스트럭터가 ).Class<E>
및 는 타입이 시킵니다.E
를 참조해 주세요.이 경우는, 다음과 같이 기입해 주세요.
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에서 제네릭스의 알려진 고의적인 약점으로부터 비롯됩니다.이는 삭제 기능을 사용하여 구현되었기 때문에 "일반" 클래스는 실행 시 어떤 유형의 인수를 사용하여 생성되었는지 알 수 없기 때문에 명시적인 메커니즘(타입 검사)이 구현되지 않는 한 유형 안전을 제공할 수 없습니다.
다음과 같이 할 수 있습니다.
E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];
이는 효과적인 Java 항목 26에서 범용 컬렉션을 구현하는 권장 방법 중 하나입니다.유형 오류도 없고 어레이를 반복 캐스팅할 필요도 없습니다.그러나 이는 잠재적으로 위험하므로 주의하여 사용해야 합니다.댓글에 자세히 써있듯이Object[]
.E[]
, 예기치 않은 나 「예기치 않은 에러」를 이 있습니다.ClassCastException
안전하지 않게 사용되는 경우.
일반적으로 이 동작은 캐스트 어레이가 내부적으로 사용되고(데이터 구조 백업 등), 클라이언트 코드에 반환되거나 노출되지 않는 한 안전합니다.코드로 , 「반사」는 「반사」입니다.Array
을 사용법
가능한 경우라면, 고객님은 보다 행복한 시간을 보내실 수 있습니다.List
을 사용하다물론 선택의 여지가 없는 경우도 있지만 컬렉션 프레임워크를 사용하는 것이 훨씬 더 강력합니다.
다음은 제네릭을 사용하여 원하는 유형의 배열을 확보하면서 유형 안전을 유지하는 방법입니다(다른 답변과 달리).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 튜토리얼에서 설명한 대로 클래스 리터럴을 런타임 유형 토큰으로 사용하여 작동합니다.클래스 리터럴은 컴파일러에 의해 의 인스턴스로 취급됩니다.java.lang.Class
를하려면 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,.class
String.class
을 하다Class
를 String
enum, 의 차원 배열enum, enum, enum, enum, any-dimension)에도 유효합니다.String[].class
primitive(예: primitive:int.class
및 )는, 「」입니다.void
(예:)void.class
Class
이다.Class<T>
서, snowledge.T
'형'은 '형의입니다.Class
representing는 오브젝트 이 나타내는 것을 합니다.String.class
Class<String>
.
래서의 를 걸면 so so so so so so so so so so so so so so so so so so so so so so so so so so so so so so so?GenSet
하고, 클래스 은, 「클래스 리터럴」의 첫 번째 인수는 「클래스 리터럴」입니다GenSet
된 유형 "Discerted Type")String[].class
★★★★★★에GenSet<String>
타입 변수에는 프리미티브를 사용할 수 없기 때문에, 프리미티브의 배열을 취득할 수 없습니다.
「」를 한다.cast
된 반환을 .Object
" "로 Class
이치노 메서드 " " 를 합니다.newInstance
java.lang.reflect.Array
로서 반환하다Object
로 대표되는 유형의 배열Class
및 ""로 의 오브젝트"int
두 번째 인수로 통과되었습니다.getComponentType
를 반환하다Class
의 오브젝트.Class
"Manager")String.class
★★★★★★에String[].class
,null
Class
개체가 배열을 나타내지 않습니다.)
그 마지막 문장은 완전히 정확하지 않다. " "String[].class.getComponentType()
를 반환하다Class
를 String
, 그 은 「」, 「」입니다Class<?>
아니라, 이에요.Class<String>
'이렇게 하면 안 돼요'라고 합니다.
String foo = String[].class.getComponentType().cast("bar"); // won't compile
방법은 입니다.Class
Class
★★★★★★ 。
이 답변에 대한 요아힘 사우어의 코멘트에 대해(본인은 코멘트를 할 만한 평판이 없습니다), 출연자를 사용한 예시는T[]
이 경우 컴파일러는 타입의 안전을 보증할 수 없기 때문에 경고가 발생합니다.
Ingo 코멘트에 대한 편집:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}
이것이 타입 세이프인 유일한 대답입니다.
E[] a;
a = newArray(size);
@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
return Arrays.copyOf(array, length);
}
, 「」를 붙이면 .[]
치수 에서 " " " "로 이동합니다newInstance()
)T
파라미터입니다.cls
는 입니다.Class<T>
,d1
through를 통해.d5
정수 : are ) ) :
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);
자세한 것은, 을 참조해 주세요.
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);
}
}
예를 들어, 이것은 에 의해 사용됩니다.
Java 8 이전 버전에서도 익명의 수업을 사용할 수 있지만, 더 번거롭습니다.
이는 유효 자바 제2판 항목 25의 제5장(일반)에 설명되어 있습니다.어레이보다 리스트 우선
코드는 동작하지만 체크되지 않은 경고(다음 주석으로 억제할 수 있음)가 생성됩니다.
@SuppressWarnings({"unchecked"})
단, 어레이 대신 목록을 사용하는 것이 좋습니다.
OpenJDK 프로젝트 사이트에서 이 오류/기능에 대한 흥미로운 설명이 있습니다.
Class 인수를 컨스트럭터에 전달할 필요는 없습니다.이거 먹어봐.
public class GenSet<T> {
private final T[] array;
@SafeVarargs
public GenSet(int capacity, T... dummy) {
if (dummy.length > 0)
throw new IllegalArgumentException(
"Do not provide values for dummy argument.");
this.array = Arrays.copyOf(dummy, 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]
Java 제네릭은 컴파일 시 유형을 체크하고 적절한 캐스트를 삽입하지만 컴파일된 파일의 유형을 지우는 방식으로 작동합니다.이것에 의해, 범용 라이브러리는, 범용적인 코드(고의적인 설계 결정)를 이해하지 못하는 코드로 사용할 수 있게 됩니다만, 통상, 실행시에 타입을 특정할 수 없습니다.
★★★★★★★★★★★★★★★★」Stack(Class<T> clazz,int capacity)
생성자는 런타임에 클래스 개체를 전달해야 합니다. 즉, 클래스 정보는 런타임에 필요한 코드를 작성할 수 있습니다.그리고 그Class<T>
Tform의 합니다.T의 서브클래스도 아니고 T의 슈퍼클래스도 아니고, 정확히는 T입니다.
이는 생성자에 적절한 유형의 배열 개체를 만들 수 있음을 의미합니다. 즉, 컬렉션에 저장하는 개체 유형은 컬렉션에 추가된 시점에서 해당 유형이 선택됨을 의미합니다.
실타래가 끊어졌지만, 이 점에 주목해 주셨으면 합니다.
제네릭은 컴파일 시 타입 체크에 사용됩니다.따라서, 그 목적은 다음과 같은 사항을 확인하는 것이다.
- 들어오는 것은 당신이 필요로 하는 것이다.
- 반품하는 것은 소비자가 필요로 하는 것입니다.
체크해 주세요.
일반 클래스를 작성할 때는 타이핑 경고에 대해 걱정하지 마십시오. 사용할 때는 걱정하십시오.
이 솔루션은 어떻습니까?
@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
return elems;
}
그것은 효과가 있고 너무 단순해 보여서 사실이 아니다.무슨 단점이 있나요?
이 예에서는 Java 리플렉션을 사용하여 배열을 만듭니다.이것은 활자판이 아니기 때문에 일반적으로 권장되지 않습니다.대신 내부 목록을 사용하여 어레이를 전혀 사용하지 않도록 해야 합니다.
다음 코드도 참조해 주세요.
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 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
// ...
}
}
어레이의 유연성을 높이기 위해 링크 목록을 사용할 수 있습니다.예를 들어, Array List 및 Java.util에 있는 다른 메서드를 사용할 수 있습니다.ArrayList 클래스
값 목록을 전달하는 중...
public <T> T[] array(T... values) {
return values;
}
간단한 자동 테스트 유틸리티로 통과된 클래스를 반영적으로 인스턴스화하기 위해 코드 스니펫을 만들었습니다.
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는 봄의 일부입니다.
다른 사람들이 제안한 강제 캐스팅은 제게 효과가 없었고, 불법 캐스팅을 예외로 했어요.
그러나 이 암묵적인 캐스팅은 잘 작동했습니다.
Item<K>[] array = new Item[SIZE];
여기서 Item은 멤버를 포함하는 정의된 클래스입니다.
private K value;
이렇게 하면 유형 K(항목에 값만 있는 경우) 또는 클래스 항목에서 정의할 일반 유형의 배열을 얻을 수 있습니다.
실제로는 오브젝트 배열을 생성하여 다음 예시와 같이 원하는 타입으로 캐스트하는 것이 보다 쉬운 방법입니다.
T[] array = (T[])new Object[SIZE];
서 ''는SIZE
로, 「」입니다.T
입니다.
당신이 올린 예에서 무슨 일이 일어나고 있는지에 대한 질문에 답한 사람은 아무도 없습니다.
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()
올바른 타입입니다(알고 있습니다만).
이 스타일은 좀 추악하지만 실행 시 컴포넌트 유형을 알아야 하는 범용 유형을 작성하는 데 가장 문제가 없는 솔루션일 수 있습니다(어레이 작성, 컴포넌트 유형의 인스턴스 작성 등).
나는 이 문제에 대한 일종의 연구를 찾았다.
다음 행은 일반 어레이 생성 오류를 발생시킵니다.
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를 클래스의 할 수 .가 입니다.List<Person>
모든 요소에서., 「」의 입니다.List<Person>
.
PersonList[] personLists=new PersonList[10];
제가 작업하던 코드에 이런 게 필요했는데 이게 바로 작동시키기 위해 제가 한 일입니다.아직까지는 문제 없습니다.
Java에서는 범용 어레이 생성이 허용되지 않지만 다음과 같이 할 수 있습니다.
class Stack<T> {
private final T[] array;
public Stack(int capacity) {
array = (T[]) new Object[capacity];
}
}
오브젝트 배열을 생성하여 모든 곳에 E로 캐스트할 수 있습니다.네, 아주 깔끔한 방법은 아니지만 적어도 효과가 있을 거예요.
이거 먹어봐.
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 Whatever<Thing>{
private class Holder<OtherThing>{
OtherThing thing;
}
public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}
제가 이 질문을 , "이 질문"은 "이 질문"을 받았습니다.generic array creation
" " " " " 를 사용 중 가 발생하였습니다
Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];
다음 작업을 (그리고 저를 위해) 조사했습니다.@SuppressWarnings({"unchecked"})
:
Tuple<Long, String>[] tupleArray = new Tuple[10];
이 코드가 효과적인 범용 어레이를 만들 수 있을까요?
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 코드를 사용하는 것만큼 다재다능하지는 않습니다.
깁스를 사용할 수 있습니다.
public class GenSet<Item> {
private Item[] a;
public GenSet(int s) {
a = (Item[]) new Object[s];
}
}
범용 어레이를 기동할 수 없는 것을 회피할 수 있는 독자적인 솔루션을 찾았습니다.다음과 같이 범용 변수 T를 받아들이는 클래스를 만들어야 합니다.
class GenericInvoker <T> {
T variable;
public GenericInvoker(T variable){
this.variable = variable;
}
}
어레이 클래스에서는 다음과 같이 시작합니다.
GenericInvoker<T>[] array;
public MyArray(){
array = new GenericInvoker[];
}
, 의new Generic Invoker[]
체크되지 않은 상태에서 문제가 발생하지만 실제로는 문제가 없을 것입니다.
어레이에서 가져오려면 어레이[i]를 호출해야 합니다.다음과 같은 변수:
public T get(int index){
return array[index].variable;
}
나머지는 Arrays.copyOf()를 사용하여 다음과 같이 어레이 크기를 조정할 수 있습니다.
public void resize(int newSize){
array = Arrays.copyOf(array, newSize);
}
추가 기능은 다음과 같이 추가할 수 있습니다.
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++;
}
고정 사이즈의 범용 어레이를 랩하는 경우는, 그 어레이에 데이터를 추가하는 방법이 있기 때문에, 다음과 같이 어레이를 적절히 초기화할 수 있습니다.
import java.lang.reflect.Array;
class Stack<T> {
private T[] array = null;
private final int capacity = 10; // fixed or pass it in the constructor
private int pos = 0;
public void push(T value) {
if (value == null)
throw new IllegalArgumentException("Stack does not accept nulls");
if (array == null)
array = (T[]) Array.newInstance(value.getClass(), capacity);
// put logic: e.g.
if(pos == capacity)
throw new IllegalStateException("push on full stack");
array[pos++] = value;
}
public T pop() throws IllegalStateException {
if (pos == 0)
throw new IllegalStateException("pop on empty stack");
return array[--pos];
}
}
이 경우 java.displect.reflect를 사용합니다.Array.newInstance를 사용하여 어레이를 만듭니다.이 인스턴스는 오브젝트[]가 아니라 실제 T[]가 됩니다.최종적이지 않은 것은 여러분의 수업에서 관리되기 때문에 걱정하지 말아야 합니다.사용하는 타입을 취득하기 위해서는 push()에 non-null 객체가 필요하기 때문에 push()에서 push하여 예외를 발생시키는 데이터에 체크를 추가했습니다.
그래도 이것은 다소 의미가 없습니다.푸시를 통해 데이터를 저장하고 T 요소만 입력하도록 보장하는 메서드의 시그니처입니다.따라서 배열이 Object[] 또는 T[]인 것은 거의 무관합니다.
vnportnoy에 따르면
GenSet<Integer> intSet[] = new GenSet[3];
null 참조 배열을 만듭니다.
for (int i = 0; i < 3; i++)
{
intSet[i] = new GenSet<Integer>();
}
안전한 타입입니다.
언급URL : https://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java
'sourcecode' 카테고리의 다른 글
Vue.js, 다른 경로의 컴포넌트에 데이터 전달 (0) | 2022.08.19 |
---|---|
엔클로징 클래스 Java가 아닙니다. (0) | 2022.08.19 |
Kotlin에서 "Activity.this"에 액세스하는 방법 (0) | 2022.08.19 |
Vue를 사용한 검색 상자 및 확인란 필터 (0) | 2022.08.19 |
JavaScript 세트에 해당하는 것은 무엇입니까?Android/Java의 Interval/setTimeout? (0) | 2022.08.19 |