List, List <?>, List <T>, List <E> 및 List <Object>의 차이점


194

의 차이점은 무엇입니까 List, List<?>, List<T>, List<E>, 그리고 List<Object>?

1. 목록

List:는 원시 유형이므로 그렇지 않습니다 typesafe. 캐스팅이 나쁜 경우에만 런타임 오류가 발생합니다. 캐스트가 나쁜 경우 컴파일 시간 오류가 필요합니다. 사용하지 않는 것이 좋습니다.

2. 목록 <?>

List<?>무제한 와일드 카드입니다. 그러나 그것이 무엇인지 잘 모르겠습니다. List<?>문제없이 인쇄 할 수 있습니다 .

public static void test(List<?> list){
    System.out.println(list);   // Works
}

에 항목을 추가 할 수없는 이유는 무엇 List<?>입니까?

public static void test(List<?> list){
    list.add(new Long(2));     // Error
    list.add("2");             // Error
    System.out.println(list);
}

3.리스트 <T>

public static void test(List<T> list){   // T cannot be resolved
    System.out.println(list);
}

이 구문을 이해하지 못합니다. 나는 이와 같은 것을 보았고 효과가있다.

public <T> T[] toArray(T[] a){
    return a;   
}

때때로, 나는 참조 <T>하거나 <E>, 또는 <U>, <T,E>. 그들은 모두 같습니까 아니면 다른 것을 나타 냅니까?

4.리스트 <개체>

이 오류는 "이 방법 test(List<Object>)은 인수에 적용 할 수 없습니다 List<String>"입니다.

public static void test(List<Object> list){
    System.out.println(list);
}

나는 다음이를하려고하면 나는 "에서 캐스팅 할 수 없습니다 얻었다 List<String>List<Object>"

test((List<Object>) names);

혼란 스러워요. String의 서브 클래스입니다 Object왜하지, List<String>의 서브 클래스 List<Object>?

답변:


77

1) 수정

2) 항목 유형에 신경 쓰지 않는 "읽기 전용"목록으로 생각할 수 있습니다. 예를 들어 목록의 길이를 반환하는 메서드에서 사용할 수 있습니다.

3) T, E 및 U는 동일하지만 사람들은 유형에 T, 요소에 E, 값에 V, 키에 K를 사용하는 경향이 있습니다. 컴파일하는 메소드는 특정 유형의 배열을 가져 와서 동일한 유형의 배열을 반환한다고 말합니다.

4) 오렌지와 사과는 섞을 수 없습니다. 문자열 목록을 개체 목록이 필요한 메서드에 전달할 수 있다면 문자열 목록에 개체를 추가 할 수 있습니다. (그리고 모든 객체가 문자열은 아닙니다)


2
의 읽기 전용 목록은 +1입니다 2. 에서 이것을 보여주기 위해 몇 가지 코드를 작성 2합니다. tyvm
Thang Pham

사람들이 왜 사용 List<Object>합니까?
Thang Pham

3
모든 유형의 항목을 허용하는 목록을 작성하는 방법이므로 거의 사용하지 않습니다.
Kaj

실제로 나는 당신이 식별자를 전혀 가질 수 없다는 것을 고려할 때 아무도 그것을 사용할 것이라고 생각하지 않을 것입니다.
if_zero_equals_one

1
@if_zero_equals_one 예, 그러나 컴파일러 경고를 받고 (원시 유형을 사용하고 있다고 경고하고) 경고와 함께 코드를 컴파일하고 싶지는 않습니다.
Kaj

26

마지막 부분 : String은 Object의 하위 집합이지만 List <String>은 List <Object>에서 상속되지 않습니다.


11
아주 좋은 지적; 많은 사람들이 클래스 C가 클래스 P에서 상속하기 때문에 List <C>도 List <P>에서 상속한다고 가정합니다. 당신이 지적했듯이 이것은 사실이 아닙니다. 그 이유는 List <String>에서 List <Object>로 캐스트 할 수 있으면 Object를 해당 목록에 넣을 수 있으므로 요소 검색을 시도 할 때 List <String>의 원래 계약을 위반하는 것입니다.
Peter

2
+1. 좋은 지적도 있습니다. 사람들이 왜 사용 List<Object>하는가?
Thang Pham

9
List <Object>는 다른 클래스의 객체 목록을 저장하는 데 사용할 수 있습니다.
Farshid Zaker

20

이 표기법 List<?>은 "뭔가의 목록 (그러나 나는 무엇을 말하지 않습니까)"을 의미합니다. 코드 test는 목록에있는 모든 종류의 객체에서 작동하므로 공식적인 메소드 매개 변수로 작동합니다.

포인트 3과 같이 타입 파라미터를 사용하려면 타입 파라미터를 선언해야합니다. 이를위한 Java 구문 <T>은 함수 앞에 놓 입니다. 이것은 메소드 본문에서 이름을 사용하기 전에 공식 매개 변수 이름을 메소드에 선언하는 것과 정확히 유사합니다.

List<Object>받아들이지 않는 것에 관해서 는 List<String>, a String가 아니기 때문에 의미 가 있습니다 Object. 의 하위 클래스입니다 Object. 수정은 선언하는 것 public static void test(List<? extends Object> set) ...입니다. 그러나 extends Object모든 클래스는 직간접 적으로 확장되기 때문에 중복됩니다 Object.


사람들이 왜 사용 List<Object>합니까?
Thang Pham

10
나는 List<?>목록이 특정하지만 알려지지 않은 유형이기 때문에 "뭔가의 목록"이 더 나은 의미라고 생각 합니다. List<Object>실제로는 무엇이든 포함 할 수 있기 때문에 "모든 것의 목록"이됩니다.
ColinD 2016 년

1
@ColinD-나는 "모든 것"이라는 의미에서 "모든 것"을 의미했습니다. 하지만 당신 말이 맞아요. 그것은 "뭔가의 목록이지만, 나는 당신에게 무엇을 말하지 않을 것"을 의미합니다.
Ted Hopp

@ColinD 그것은 왜 당신이 그의 말을 반복 했습니까? 네, 조금 다른 단어로 쓰여졌지만 그 의미는 같습니다.
user25

14

당신이 캐스팅 할 수없는 이유 List<String>에는 List<Object>당신이의 제약을 위반하는 것을 허용 할 것입니다 List<String>.

다음 시나리오를 생각해보십시오.가있는 경우 List<String>유형의 객체 만 포함해야합니다 String. ( final클래스 인)

이를에 캐스트 할 수 있으면 해당 목록 List<Object>에 추가 Object하여의 원래 계약을 위반할 수 List<String>있습니다.

따라서 일반적으로 클래스 C 클래스의 상속은 P, 당신이 말할 수 없다 GenericType<C>또한 상속 GenericType<P>.

NB 나는 이미 이전 답변에서 이것에 대해 언급했지만 그것을 확장하고 싶었습니다.


tyvm, 나는 귀하의 의견과 답변을 모두 업로드합니다. 사람들이 어디에서 왜 사용 List<Object>합니까?
Thang Pham

3
일반적으로 List<Object>제네릭의 목적을 무너 뜨리기 때문에 사용해서는 안됩니다 . 그러나 이전 코드에 List다른 유형 이 허용 되는 경우가 있으므로 원시 유형에 대한 컴파일러 경고를 피하기 위해 유형 매개 변수화를 사용하도록 코드를 개조하는 것이 좋습니다. (그러나 기능은 바뀌지 않았습니다)
Peter


5

자바 역사의 맥락에서 그들에 대해 이야기하자;

  1. List:

목록은 모든 객체를 포함 할 수 있음을 의미합니다. 리스트는 Java 5.0 이전의 릴리스에있었습니다. Java 5.0은 이전 버전과의 호환성을 위해 List를 도입했습니다.

List list=new  ArrayList();
list.add(anyObject);
  1. List<?>:

?알 수없는 객체를 의미합니다. 와일드 카드 ?소개는 Generic Type으로 작성된 문제를 해결하기위한 것입니다. 와일드 카드 참조 ; 그러나 이것은 또 다른 문제를 일으킨다 :

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  1. List< T> List< E>

프로젝트 Lib에 T 또는 E 유형이 없음을 전제로 일반 선언을 의미합니다.

  1. List< Object> 일반 매개 변수화를 의미합니다.

5

세 번째 관점에서, "T"는 선언되지 않았기 때문에 해결할 수 없습니다. 일반적으로 일반 클래스를 선언 할 때 "T"를 바인딩 된 유형 매개 변수 의 이름으로 사용할 수 있습니다. 오라클의 자습서를 포함한 많은 온라인 예제 에서는 "T"를 예를 들어 type 매개 변수의 이름은 다음과 같은 클래스를 선언합니다.

public class FooHandler<T>
{
   public void operateOnFoo(T foo) { /*some foo handling code here*/}

}

당신이 것을 말하는 FooHandler's operateOnFoo방법을 염두에두고, 클래스 선언 자체에 선언 된 유형 "T"의 변수를 기대하고, 나중에 같은 다른 방법을 추가 할 수 있습니다

public void operateOnFoos(List<T> foos)

모든 경우에 T, E 또는 U에 유형 매개 변수의 모든 식별자가 있으며 구문을 사용하는 둘 이상의 유형 매개 변수를 가질 수도 있습니다.

public class MyClass<Atype,AnotherType> {}

네 번째 ponint에서 Sting은 Object의 하위 유형이지만 generics 클래스에는 그러한 관계 List<String>가 없으며 하위 유형이 아니며 List<Object>컴파일러 관점에서 두 가지 유형이 있습니다. 이 블로그 항목 에서 가장 잘 설명됩니다.


5

이론

String[] 에 던져 질 수있다 Object[]

그러나

List<String>에 캐스팅 할 수 없습니다 List<Object>.

연습

리스트의 경우 컴파일 타임 에 메소드에 전달 된 List 매개 변수의 유형이 확인되지 않기 때문에 그보다 미묘 합니다. 메소드 정의 List<?>는 컴파일러의 관점에서 볼 때 동등합니다. 이것이 OP의 예제 # 2가 컴파일 오류가 아닌 런타임 오류를 제공하는 이유입니다.

List<Object>메소드에 전달 된 매개 변수를주의해서 처리 하여 목록의 요소에 대해 유형 점검을 강요하지 않으면 메소드를 사용하여 정의 할 수 List<Object>있지만 실제로 List<String>는 호출 코드에서 매개 변수를 승인 할 수 있습니다 .

A. 따라서이 코드는 컴파일 또는 런타임 오류를 발생시키지 않으며 실제로 (그리고 놀랍게도) 작동합니다.

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test(argsList);  // The object passed here is a List<String>
}

public static void test(List<Object> set) {
    List<Object> params = new ArrayList<>();  // This is a List<Object>
    params.addAll(set);       // Each String in set can be added to List<Object>
    params.add(new Long(2));  // A Long can be added to List<Object>
    System.out.println(params);
}

B. 이 코드는 런타임 오류를 발생시킵니다 :

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test1(argsList);
    test2(argsList);
}

public static void test1(List<Object> set) {
    List<Object> params = set;  // Surprise!  Runtime error
}

public static void test2(List<Object> set) {
    set.add(new Long(2));       // Also a runtime error
}

C. 이 코드는 런타임 오류를 발생시킵니다 ( java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]) :

public static void main(String[] args) {
    test(args);
}

public static void test(Object[] set) {
    Object[] params = set;    // This is OK even at runtime
    params[0] = new Long(2);  // Surprise!  Runtime error
}

B에서 매개 변수 setList컴파일 타임에 입력되지 않습니다 List<?>. 컴파일러는 이 매개 변수를 로 간주합니다 . 런타임시,에서 set전달 된 실제 객체가 되기 때문에 런타임 오류 main()가 있습니다 List<String>. 에 List<String>캐스팅 할 수 없습니다 List<Object>.

C에서 매개 변수 set는을 요구합니다 Object[]. String[]개체를 매개 변수로 사용하여 호출하면 컴파일 오류와 런타임 오류가 없습니다 . 에 String[]캐스팅하기 때문 Object[]입니다. 그러나 실제 수신 된 객체는로 test()유지되며 String[]변경되지 않았습니다. 따라서 params객체도 String[]. 그리고의 요소 0 String[]A를 할당 할 수 없습니다 Long!

(나의 추론이 틀렸다면 커뮤니티가 나에게 말할 것이라고 확신합니다. 업데이트 : 예제 A의 코드를 업데이트하여 실제로 컴파일하면서 요점을 보여주었습니다.)


나는 당신의 예제 A를 시도했지만 작동 하지 않습니다 : List<Object> cannot be applied to List<String>. 당신은 할 수없는 통과 ArrayList<String>예상하는 방법 ArrayList<Object>.
parsecer

고맙게도, 나는 예제 A를 조정하여 날짜가 늦어졌습니다. 주요 변경 사항은 main ()에서 argsList를 일반적으로 정의하는 것이 었습니다.
radfast

4

"System.out.println (set);"때문에 문제 2는 정상입니다. "System.out.println (set.toString ());"을 의미합니다. set은 List의 인스턴스이므로 complier는 List.toString ()을 호출합니다.

public static void test(List<?> set){
set.add(new Long(2)); //--> Error  
set.add("2");    //--> Error
System.out.println(set);
} 
Element ? will not promise Long and String, so complier will  not accept Long and String Object

public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2");    //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object

문제 3 : 이러한 기호는 동일하지만 다른 사양을 지정할 수 있습니다. 예를 들면 다음과 같습니다.

public <T extends Integer,E extends String> void p(T t, E e) {}

문제 4 : 수집은 유형 모수 공분산을 허용하지 않습니다. 그러나 배열은 공분산을 허용합니다.


0

당신이 맞습니다 : String은 Object의 하위 집합입니다. String은 Object보다 "정확한"것이므로 System.out.println ()의 인수로 사용하도록 캐스트해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.