Java 언어는 다음과 같은 종류의 실제 유형 인수를 지원합니다.
- 구체적 유형 : 클래스 또는 기타 참조 유형 이름이 유형 매개 변수에 전달됩니다. 예를 들어,에
List<Animal>
,Animal
에 전달됩니다E
. - 구체적 매개 변수화 된 형식 : 매개 변수화 된 형식 이름이 형식 매개 변수에 전달됩니다. 예를 들어,에
Set<List<Shape>>
,List<Shape>
에 전달됩니다E
. - 배열 유형 : 배열이 type 매개 변수에 전달됩니다. 예를 들어,에
Map<String, String[]>
,String
에 전달K
하고String[]
에 전달됩니다V
. - 유형 매개 변수 : 유형 매개 변수가 유형 매개 변수에 전달됩니다. 예를 들어,에
class Container<E> { Set<E> elements; }
,E
에 전달됩니다E
. - 와일드 카드 : 물음표 (
?
)가 type 매개 변수에 전달됩니다. 예를 들어,에Class<?>
,?
에 전달됩니다T
.
각 generic 형식은 원시 형식 의 존재를 의미 하며 형식 형식 매개 변수 목록이없는 제네릭 형식입니다. 예를 들어, Class
원시 유형이 Class<T>
있습니다. 제네릭 형식과 달리 원시 형식은 모든 종류의 개체와 함께 사용할 수 있습니다.
제네릭 형식 선언 및 사용
제네릭 형식을 선언하는 데는 형식 형식 매개 변수 목록을 지정하고 해당 형식 매개 변수를 해당 구현을 통해 사용하는 것이 포함됩니다. 제네릭 형식을 사용하면 제네릭 형식을 인스턴스화 할 때 실제 형식 인수를 해당 형식 매개 변수에 전달해야합니다. Listing 5를 참조한다.
Listing 5. GenDemo.java (버전 1)
Listing 5는 적절한 인수 유형의 객체를 저장하는 간단한 컨테이너 유형의 컨텍스트에서 일반 유형 선언 및 사용법을 보여줍니다. 코드를 간단하게 유지하기 위해 오류 검사는 생략했습니다.
Container
클래스는 지정하여 제네릭 형식을로 자신을 선언 <E>
형식 형식 매개 변수 목록을. Type 매개 변수 E
는 저장된 요소의 유형, 내부 배열에 추가 할 요소 및 요소를 검색 할 때의 반환 유형을 식별하는 데 사용됩니다.
Container(int size)
생성자를 통해 상기 배열을 생성 elements = (E[]) new Object[size];
. 왜 내가 지정하지 않았는지 궁금하다면 그 이유 elements = new E[size];
는 불가능하다는 것입니다. 그렇게하면 a ClassCastException
.
Listing 5 (컴파일 javac GenDemo.java
). (E[])
캐스트 출력 캐스트가 선택하지 않은 것에 대해 경고를 컴파일러가 발생합니다. 그것은 플래그에서 downcasting 가능성 Object[]
에 대한 것이 E[]
있기 때문에 유형의 안전을 위반할 수 Object[]
개체의 모든 종류를 저장할 수 있습니다.
그러나이 예제에서는 형식 안전을 위반할 수있는 방법이 없습니다. E
내부 배열에 비 객체 를 저장하는 것은 불가능 합니다. 다음 기사에서이 경고 메시지를 표시하지 않는 방법을 알려 드리겠습니다.
실행 java GenDemo
이 응용 프로그램을 실행합니다. 다음 출력을 관찰해야합니다.
경계 유형 매개 변수
E
에서이 Set<E>
의 예입니다 바운드 형식의 형식 매개 변수 당신이 어떤 실제의 형태 인수를 전달할 수 있기 때문에 E
. 예를 들어, 당신은 지정할 수 있습니다 Set<Marble>
, Set<Employee>
또는 Set<String>
.
경우에 따라 형식 매개 변수로 전달할 수있는 실제 형식 인수의 형식을 제한하려고합니다. 예를 들어, type 매개 변수를 accept Employee
및 해당 하위 클래스 로 제한 하려고합니다.
사용자가 지정하여 입력 파라미터를 제한 할 수있는 상한을 실제의 입력 인자로 전달 될 수있는 유형의 상한로서의 형태이다. 예약어를 통해 상한을 지정하고 extends
그 뒤에 상한의 유형 이름을 지정하십시오.
예를 들어, class Employees<E extends Employee>
전달 될 수있는 유형의 제한 Employees
에 Employee
또는 서브 클래스 (예를 Accountant
). 지정 new Employees<Accountant>
하는 것은 합법적 인 반면 new Employees<String>
불법입니다.
유형 매개 변수에 둘 이상의 상한을 지정할 수 있습니다. 그러나 첫 번째 경계는 항상 클래스 여야하며 추가 경계는 항상 인터페이스 여야합니다. 각 바운드는 앰퍼샌드 ( &
) 에 의해 전임자와 구분됩니다 . Listing 6을 확인하자.
Listing 6. GenDemo.java (버전 2)
Listing 6의 Employee
클래스는 시간당 급여를받는 직원의 개념을 추상화한다. 이 클래스는 하위 클래스로 Accountant
, s가 자연 순서에 따라 비교 될 수 있음 Comparable<Accountant>
을 나타 내기 Accountant
위해 구현됩니다.이 순서는이 예에서 시간별 임금입니다.
java.lang.Comparable
인터페이스의 이름은 단일 유형의 매개 변수를 사용하여 제네릭 형식으로 선언됩니다 T
. 이 클래스 int compareTo(T o)
는, 현재의 객체와 인수의 형태 T
를 비교해,이 객체가 지정된 객체보다 작은 경우는 부의 정수, 동일한 경우는 0, 큰 경우는 정의 정수를 돌려주는 메소드를 제공합니다.
이 SortedEmployees
클래스를 사용하면 내부 배열 Employee
에 구현 Comparable
된 하위 클래스 인스턴스를 저장할 수 있습니다 . 이 배열은 서브 클래스 인스턴스가 추가 된 후 시간당 임금의 오름차순 으로 ( java.util.Arrays
클래스의 void sort(Object[] a, int fromIndex, int toIndex)
클래스 메소드 를 통해) 정렬 Employee
됩니다.
Listing 6 ( javac GenDemo.java
)을 컴파일 하고 애플리케이션 ( java GenDemo
) 을 실행한다 . 다음 출력을 관찰해야합니다.
하한은 어떨까요?
제네릭 형식 매개 변수의 하한을 지정할 수 없습니다. 왜 내가 Angelika Langer의 Java Generics FAQ 를 "하찮은 것이고 특별히 도움이되지 않을 것"이라고 말하는 낮은 범위의 주제에 대해 읽는 것이 좋은지 이해하는 것.
와일드 카드 고려하기
이러한 개체가 문자열, 직원, 도형 또는 기타 형식인지 여부에 관계없이 개체 목록을 인쇄하려고한다고 가정 해 봅시다. 첫 번째 시도는 Listing 7과 같다.
Listing 7. GenDemo.java (버전 3)
문자열 목록이나 정수 목록은 객체 목록의 하위 유형이지만 컴파일러는이 목록을 컴파일하려고하면 불만을 나타냅니다. 특히 그것은 list-of-string이 list-of-object로 변환 될 수없고 integer-of-list와 마찬가지로 변환 될 수 있음을 알려줍니다.
받은 오류 메시지는 제네릭의 기본 규칙과 관련되어 있습니다.
y 유형의 지정된 부속 유형 x 에 대해 G 를 원시 유형 선언으로 지정하면 G <x> 는 G <y> 의 부속 유형이 아닙니다 .
하지만,이 규칙에 따르면 String
과 java.lang.Integer
의 하위 유형이있다 java.lang.Object
, 그것은 사실이 아니에요 List<String>
과 List<Integer>
의 하위 유형이 있습니다 List<Object>
.
왜 우리는이 규칙을 가지고 있습니까? 제네릭은 컴파일 타임에 유형 안전 위반 사항을 파악하도록 설계되었으므로 도움이됩니다. 제네릭 없이는 Java 프로그램이 중단 ClassCastException
되어 충돌 이 발생하여 오전 2시에 작업하라는 요청이 훨씬 많이 발생할 수 있습니다 !
데모로, 이제 그 가정 해 봅시다 List<String>
있었다 의 하위 유형 List<Object>
. 이것이 사실이라면 다음 코드로 끝날 수 있습니다.
이 코드 조각은 배열 목록을 기반으로 문자열 목록을 만듭니다. 그런 다음이 목록을 객체 목록에 업 캐스팅합니다 (이는 합법이 아니지만 지금은 그냥 그대로있는 것입니다). 그런 다음 객체 목록에 정수를 추가하여 유형 안전을 위반합니다. ClassCastException
저장된 정수를 문자열로 캐스트 할 수 없기 때문에 문제가 발생 합니다.
generics가 없다면 유형 안전성 위반을 막을 수있는 유일한 방법 은 유형 7 List<Object>
의 printList()
메소드에 유형의 오브젝트를 전달하는 것이고 이는 그리 유용하지 않을 것이다. 그러나 generics 를 사용하면 Listing 8과 같이 와일드 카드를 사용하여 문제를 해결할 수있다.
Listing 8. GenDemo.java (버전 4)
목록 8에서는 내가 사용하는 와일드 카드 합니다 ( ?
대신에 기호) Object
에서 printList()
의 매개 변수 목록과 몸. 이 기호는 모든 유형을 의미하기 때문에, 전달하는 합법적 List<String>
및 List<Integer>
이 방법에.
Listing 8 ( javac GenDemo.java
)을 컴파일 하고 애플리케이션 ( java GenDemo
) 을 실행한다 . 다음 출력을 관찰해야합니다.
일반적인 방법 발견하기
이제 필터 목록에 따라 개체 목록을 다른 목록에 복사한다고 가정 해 보겠습니다. void copy(List<Object> src, List<Object> dst, Filter filter)
메소드를 선언하는 것을 고려할 수도 있지만,이 메소드는 List
s의 Object
s 만을 복사 할 수 있습니다 .
임의 유형의 소스 및 대상 목록을 전달하려면 유형 자리 표시 자에 대해 와일드 카드를 사용해야합니다. 예를 들어, 다음 copy()
메소드를 고려하십시오 .
이 메소드의 매개 변수 목록은 정확하지만 문제가 있습니다. 컴파일러에 따르면 dest.add(src.get(i));
유형 안전을 위반합니다. 이는 ?
모든 종류의 객체가 목록의 요소 유형이 될 수 있으며 소스와 대상 요소 유형이 호환되지 않을 수 있음을 의미합니다.
예를 들어, 소스 목록이었다 List
의 Shape
와 대상 목록이 있었다 List
의 String
, 그리고 copy()
진행시키고 ClassCastException
대상 목록의 요소를 검색 할 때 발생된다.
다음과 같이 와일드 카드의 상한 및 하한을 제공하여이 문제를 부분적으로 해결할 수 있습니다.