2010-12-02 7 views
11

두 줄 목록을 배열 변환을 한 줄로 바꾸는 도우미 메서드를 작성하려고합니다. 내가 만난 문제는 T [] 인스턴스를 만드는 방법을 잘 모르는 것입니다.제네릭 메서드에서 제네릭 배열 인스턴스 만들기

내가

Array.newInstance(T.class, list.size)

을 시도했지만 나는 그것을 T.class를 공급되지 않습니다 ..

또한 new T[](list.size)을 시도하지만 매개 변수를 좋아하지 않는다.

public <T> T[] ConvertToArray(List<T> list) 
{ 
    T[] result = ??? 

    result = list.toArray(result); 

    return result; 
} 

다른 아이디어? 당신이 T.class에 전달할 수없는 경우

감사

+0

내 ToArray S.P를 수정 해 주셔서 감사합니다. 자연스러운 C# 개발자라고 말할 수 있습니까? : D –

답변

12

그런 제네릭과 배열을 혼합 할 수는 없습니다. Generics에는 컴파일 타임 검사가 있고 배열에는 런타임 검사가 있으며 이러한 접근법은 대부분 호환되지 않습니다. 처음에 나는 다음과 같이 제안했다 :

@SuppressWarnings("unchecked") 
public <T> T[] ConvertToArray(List<T> list) 
{  
    Object[] result = new Object[list.size()]; 
    result = list.toArray(result); 
    return (T[])result; 
} 

이것은 적어도 은사가 작동한다고 생각했기 때문에 은밀한 방식으로 잘못되었다. 그러나 Object []를 Integer []로 형 변환 할 수 없기 때문에 호환되지 않는 형식 오류가 발생합니다. 왜 T.class를 가져 와서 올바른 타입의 배열을 만들 수 없습니까? 아니면 new T[]을할까요?

Generics는 이전 버전과의 호환성을 유지하기 위해 유형 삭제 인 을 사용합니다. 컴파일시에 검사되지만 런타임에서 제거되므로 바이트 코드는 제네릭 이전 JVM과 호환됩니다. 즉, 런타임시 일반 변수에 대한 지식을 습득 할 수 없습니다.

그래서 당신은 T[] result이 유형이 될 것이라는 점을 보장 할 수있는 동안 정수 [] 미리 코드 list.toArray(result); (또는 new T[], 또는 Array.newInstance(T.class, list.size());)는 런타임에만 발생하며,이 T가 무엇인지 알 수 없다!

public static <T> T[] convertToArray(List<?> list, Class<T> c) { 
    @SuppressWarnings("unchecked") 
    T[] result = (T[]) Array.newInstance(c, list.size()); 
    result = list.toArray(result); 
    return (T[]) result; 
} 

주 우리는 실행시 (뿐만 아니라 제네릭을 통해 컴파일시에 클래스를 제공하는 두 번째 매개 변수가 : 여기

그 강의를 읽고에 대한 보상으로, 작업을 수행 버전입니다). 다음과 같이 이것을 사용하십시오 :

Integer[] arrayOfIntegers = convertToArray(listOfIntegers, Integer.class); 

이 가치는 있습니까? 우리는 여전히 경고를 억제 할 필요가 있으므로 확실히 안전합니까?

내 대답은 예입니다. 거기에서 생성 된 경고는 컴파일러에서 "나는 확실하지 않다"입니다. 단계별로 살펴보면 해당 캐스트가 항상 성공한다는 것을 확인할 수 있습니다. 두 번째 매개 변수로 잘못된 클래스를 넣더라도 컴파일 타임 경고가 throw됩니다.

이렇게하는 것이 가장 큰 이점은 우리가 한 곳으로 경고를 집중화했다는 것입니다. 우리는이 한 곳을 정확하게 증명할 필요가 있으며 코드가 항상 성공한다는 것을 알고 있습니다.

전체 응용 프로그램이 1.5 -source javac의를 사용하여 확인 경고없이 컴파일 된 경우 언어가 보장하도록 설계

, 지금보다는 그래서 안전 [1]

를 입력 할 수 있습니다 : 자바 문서를 인용 코드 전체에 이러한 경고가 표시되므로 한 곳에서만 사용할 수 있으므로 걱정없이 사용할 수 있습니다. 실수로 사용하는 위험이 크게 줄어 듭니다.

this SO answer 더 자세한 내용을 설명하고 this answer을 쓰는 것이 내 침대 시트였습니다. already cited Java documentation과 마찬가지로 다른 유용한 참고 자료는 Sun Microsystems의 선임 직원 엔지니어이자 1.4 및 5.0의 언어 기능 공동 설계자 인 Neal Gafter의 this blog post입니다.

물론 ShaneC 덕분에 처음 대답이 런타임에 실패했다고 올바르게 지적했습니다.

+0

이 문제는 Integer [] results = convertToArray (listOfIntegers)라고하면 java.lang.Object를 java.lang.Integer로 캐스팅 할 수 없다는 오류가 발생합니다. –

+0

ShaneC가 정확합니다. 작동하지 않습니다. 죄송합니다. 제가 작성한 자바 컴파일러가 없었습니다! – ZoFreX

+0

수정 됨! 감사합니다 쉐인! – ZoFreX

2

는, 당신은 기본적으로 끝장. 형식 지움은 실행시에 T의 형식을 알 수 없다는 것을 의미합니다.

물론 super type tokens과 같이 유형을 지정하는 다른 방법이 있습니다. 그러나 T.class을 전달할 수 없으면 유형 토큰 중 하나를 전달할 수 없습니다. 그렇게 할 수 있다면 큰 일 이겠지요 :)

+0

그건 그냥 내게 너무 잔인한 것 같아 .. * 한숨 * –

+0

@ 에드 : 고마워, 고마워. @ ShaneC : 타입 지우기는 기본적으로 ... 특히 .NET의 매우 다른 generics 구현에 익숙하다면 말이다. –

+0

@ 존 스 켈넷 (Skeet)은 크로스 플랫폼/마이크로 소프트가없는 C#의 오픈 소스 버전을 어디서든 사용할 수 있으며, 즉시 뛰어 넘을 것입니다. –

1

문제는 유형 삭제 때문에 List가 런타임에 구성 요소 유형을 알지 못하는 반면 구성 요소 유형은 배열을 작성하는 데 필요한 것입니다.

배열의 구성 요소 클래스 ( as suggested by Jon Skeet) 또는
  • 에서
    • 패스 객체 [] 배열을 생성하고 (as suggested by ZoFrex)
    • 캐스팅 : 그래서

      당신은 당신이 모든 API를 통해 찾을 수 두 가지 옵션이 있습니다

    내가 생각할 수있는 유일한 가능성은 것 거대한 번거 로움 :

    • 목록의 모든 항목을 반복하고 모든 항목이 확장하거나 구현하는 가장 구체적인 클래스 또는 인터페이스 인 "Greatest Divisor Divisor"를 찾으십시오.
    • 이 유형

    의 배열을 생성하지만 그보다 더 많은 라인이 될 것 당신이 (그리고 그것은 또한 클라이언트 코드가 유효하지 않은 가정을 만들기로 이어질 수 있음).

  • +0

    내 방식으로는 작동하지 않으므로 하나의 옵션 만 있습니다. – ZoFreX

    +0

    업데이트 : 내 방식이 수정되었지만 Jon Skeet의 답변과 동일합니다. 여전히 유일한 옵션. – ZoFreX

    +0

    이 방법은 'List '에'Cat' 인스턴스 만있는 경우'Animal []'대신'Cat []'을 반환하여 다른 코드가 스틱하려고 할 때 실패하게하는 문제가 있습니다 결과 배열에 'Dog'를 추가합니다. – Gabe

    -1

    내가 원하는대로 할 수있는 가장 가까운 곳입니다. 이렇게하면 코드를 작성할 때 클래스를 알 수 있고 목록의 모든 객체가 동일한 클래스 (즉, 하위 클래스 없음)라는 큰 가정이 생깁니다. 이것은 하위 클래스에 대한 가정을 해소하기 위해 좀더 정교해질 수 있다고 확신하지만, 자바에서 코딩 시간에 클래스를 알고 있다는 가정을 잊을 수는 없다.

    package play1; 
    
    import java.util.*; 
    
    public class Play 
    { 
        public static void main (String args[]) 
        { 
        List<String> list=new ArrayList<String>(); 
        list.add("Hello"); 
        list.add("Shalom"); 
        list.add("Godspidanya"); 
    
        ArrayTool<String> arrayTool=new ArrayTool<String>(); 
        String[] array=arrayTool.arrayify(list); 
    
        for (int x=0;x<array.length;++x) 
        { 
         System.out.println(array[x]); 
        } 
        } 
    } 
    class ArrayTool<T> 
    { 
        public T[] arrayify(List<T> list) 
        { 
        Class clazz=list.get(0).getClass(); 
        T[] a=(T[]) Array.newInstance(clazz, list.size()); 
        return list.toArray(a); 
        } 
    } 
    
    0

    정말 한 줄자로 변경해야합니까? 어떤 구체적인 클래스로, 당신은 이미 한 줄에 배열 변환에 대한 목록 작업을 수행 할 수 있습니다

    MyClass[] result = list.toArray(new MyClass[0]); 
    

    부여,이 클래스의 일반적인 인수 작동하지 않습니다.

    Joshua Bloch의 Effective Java Second Edition, 항목 25 : 배열 목록 선호 (pp 119-123)를 참조하십시오. sample chapter PDF의 일부입니다.

    +0

    당신은'MyClass [] result = list.toArray (new MyClass [list.size()]);'를 수행해야합니다. 그러면 전달 된 배열을 재사용 할 수 있습니다. –