Java에서의 범용 기능
Generics in Java제네릭스는 2004년 J2SE 5.0 버전에서 Java 프로그래밍 언어에 추가된 범용 프로그래밍 기능입니다.이들은 자바 타입 시스템을 확장하여 "컴파일 타임 타입의 [1]안전성을 제공하면서 다양한 타입의 오브젝트 상에서 동작하는 타입 또는 방법"을 허용하도록 설계되었다.2016년에 모든 [2]경우에 보장되지 않는 것으로 나타났기 때문에 측면 컴파일 시간형 안전성은 완전히 달성되지 않았다.
Java 컬렉션 프레임워크는 컬렉션인스턴스에 저장되는 오브젝트 유형을 지정할 수 있는 범용 기능을 지원합니다.
1998년 Gilad Bracha, Martin Odersky, David Stoutamire 및 Philip Wadler는 범용 [3]유형을 지원하기 위해 Java 언어의 확장인 Generic Java를 만들었습니다.범용 자바는 와일드카드를 추가하여 Java에 통합되었습니다.
계층 및 분류
Java Language Specification(Java 언어 사양)[4]에 따라:
- 유형 변수는 정규화되지 않은 식별자입니다.유형 변수는 범용 클래스 선언, 범용 인터페이스 선언, 범용 메서드 선언 및 범용 컨스트럭터 선언에 의해 도입됩니다.
- 클래스는 하나 이상의 유형 변수를 선언하는 경우 일반적입니다.이러한 유형 변수를 클래스의 유형 매개 변수라고 합니다.매개 변수로 작동하는 하나 이상의 유형 변수를 정의합니다.범용 클래스 선언은 타입 파라미터 섹션의 가능한 호출마다 하나씩 파라미터화된 타입의 세트를 정의합니다.이러한 매개 변수화된 유형은 모두 런타임에 동일한 클래스를 공유합니다.
- 인터페이스는 하나 이상의 유형 변수를 선언하는 경우 일반적입니다.이러한 타입 변수는 인터페이스의 타입 파라미터라고 불립니다.매개 변수로 작동하는 하나 이상의 유형 변수를 정의합니다.범용 인터페이스 선언은 타입 파라미터 섹션의 가능한 호출마다1개의 타입 세트를 정의합니다.파라미터화된 모든 유형은 런타임에 동일한 인터페이스를 공유합니다.
- 메서드는 하나 이상의 유형 변수를 선언하는 경우 일반적입니다.이러한 유형 변수를 메서드의 형식 유형 매개 변수라고 합니다.형식 유형 파라미터 리스트의 형식은 클래스 또는 인터페이스의 유형 파라미터 리스트와 동일합니다.
- 생성자가 선언된 클래스가 일반인지 여부에 관계없이 생성자를 일반으로 선언할 수 있습니다.생성자는 하나 이상의 유형 변수를 선언하는 경우 일반적입니다.이러한 유형 변수를 생성자의 형식 유형 매개 변수라고 합니다.형식 유형 파라미터 리스트의 형식은 범용 클래스 또는 인터페이스의 유형 파라미터 리스트와 동일합니다.
동기
다음 Java 코드 블록은 제네릭을 사용하지 않을 때 발생하는 문제를 보여 줍니다.첫 번째로, 이 명령어는ArrayList
타입의Object
그 후, A가 추가된다.String
에게ArrayList
마지막으로 추가된 데이터 취득을 시도합니다.String
그리고 그것을 에 던지다Integer
: 일반적으로 임의의 문자열을 정수로 캐스팅할 수 없기 때문에 로직 오류입니다.
목록. v = 신규 어레이 리스트(); v.더하다('테스트"); // 정수로 캐스트할 수 없는 문자열 정수 i = (정수)v.얻다(0); // 런타임 오류
코드는 오류 없이 컴파일되지만 런타임 예외가 발생합니다.java.lang.ClassCastException
코드 3행째를 참조해 주세요.이러한 유형의 로직 오류는 제네릭을 사용하여 컴파일 중에 검출될 수 있으며 이러한 로직 오류를 사용하는 주된 동기입니다.
위의 코드 조각은 제네릭을 사용하여 다음과 같이 다시 작성할 수 있습니다.
목록.< >스트링> v = 신규 어레이 리스트< >스트링>(); v.더하다('테스트"); 정수 i = (정수)v.얻다(0); // (유형 오류) 컴파일 시간 오류
type 파라미터String
꺽쇠 괄호 안에서는ArrayList
구성되다String
(의 후예)ArrayList
의 범용Object
컴포넌트).제네릭스의 경우, 3행째를 특정 타입에 입력할 필요가 없습니다.왜냐하면,v.get(0)
로 정의됩니다.String
컴파일러에 의해 생성된 코드에 의해 결정됩니다.
이 fragment의 세 번째 줄에 있는 논리적 결함은 컴파일러가 다음을 탐지하기 때문에 컴파일 시간 오류(J2SE 5.0 이상)로 탐지됩니다.v.get(0)
돌아온다String
대신Integer
보다 상세한 예에 대해서는,[5] 참조를 참조해 주세요.
다음은 인터페이스의 정의에서 발췌한 것입니다.List
그리고.Iterator
패키지:
일반의 인터페이스 목록.< >E> { 무효 더하다(E x); 반복기< >E> 리터레이터(); } 일반의 인터페이스 반복기< >E> { E 다음 분.(); 부울 다음(); }
와일드카드 입력
파라미터화된 유형의 type 인수는 구체적인 클래스 또는 인터페이스로 제한되지 않습니다.Java를 사용하면 유형 와일드카드를 매개 변수화된 유형의 유형 인수로 사용할 수 있습니다.와일드카드는 " 형식의 인수입니다.<?>
"; 옵션으로 상한 또는 하한을 지정합니다.와일드카드로 표시되는 정확한 유형을 알 수 없기 때문에 파라미터화된 유형을 사용하는 개체에서 호출할 수 있는 메서드 유형에 제한이 있습니다.
다음 예시는 의 요소 유형이Collection<E>
는 와일드카드로 파라미터화 됩니다.
수집<?> c = 신규 어레이 리스트< >스트링>(); c.더하다(신규 물건()); // 컴파일 시간 오류 c.더하다(무효); // 허용
어떤 종류의 원소가 있는지 모르기 때문에c
오브젝트를 추가할 수 없습니다.그add()
method는 type 인수를 사용합니다.E
, 의 요소 유형Collection<E>
범용 인터페이스실제 type 인수가?
알 수 없는 유형을 나타냅니다.에 전달되는 메서드 인수 값add()
method는 이 알 수 없는 유형의 하위 유형이어야 합니다.어떤 타입인지 모르기 때문에 아무것도 전달할 수 없습니다.유일한 예외는 null입니다.모든 [6]유형의 멤버입니다.
유형 와일드카드의 상한을 지정하려면extends
type 인수가 바운딩 클래스의 서브타입임을 나타내기 위해 키워드를 사용합니다.그렇게List<? extends Number>
지정된 목록에 알 수 없는 유형의 객체가 포함되어 있는 것을 의미합니다.Number
예를 들어, 리스트는 다음과 같습니다.List<Float>
또는List<Number>
리스트에서 요소를 읽으면Number
. null 요소 추가도 [7]허용됩니다.
위의 와일드카드를 사용하면 구체적인 타입을 type 인수로 하는 파라미터화된2가지 타입 사이에는 상속관계가 없기 때문에 유연성이 높아집니다.둘 다 아니다.List<Number>
도 아니다List<Integer>
다른 타입의 하위 타입입니다.Integer
의 서브타입입니다.Number
그래서, 어떤 방법이든List<Number>
파라미터는 인수를 받아들이지 않기 때문에List<Integer>
이 경우 삽입할 수 있습니다.Number
그건...Integer
타입의 안전을 침해하는 거죠이 예는 다음과 같은 경우 유형 안전이 어떻게 침해될 수 있는지를 보여줍니다.List<Integer>
의 하위 유형이었다.List<Number>
:
목록.< >정수> 인츠 = 신규 어레이 리스트< >정수>(); 인츠.더하다(2); 목록.< >번호> 숫자 = 인츠; // 목록일 경우 유효합니다.Integer >는 대체 규칙에 따라 List <Number >의 서브타입이었습니다. 숫자.더하다(3.14); 정수 x = 인츠.얻다(1); // 이제 3.14가 Integer 변수에 할당되었습니다!
와일드카드를 사용한 솔루션은 타입의 안전을 위반하는 조작을 허가하지 않기 때문에 기능합니다.
목록.<? 확장 번호> 숫자 = 인츠; // OK(확인) 숫자.더하다(3.14); // 컴파일 시간 오류 숫자.더하다(무효); // 허용
와일드카드 타입의 하한 클래스를 지정하려면super
키워드가 사용됩니다.이 키워드는 type 인수가 바운딩 클래스의 슈퍼타입임을 나타냅니다.그렇게,List<? super Number>
대표할 수 있다List<Number>
또는List<Object>
. 다음과 같이 정의된 목록에서 읽기List<? super Number>
유형의 요소를 반환합니다.Object
. 이러한 목록에 추가하려면 유형 요소 중 하나가 필요합니다.Number
, 의 임의의 서브타입Number
또는 null(모든 유형의 멤버)입니다.
Joshua Bloch의 저서 Effective Java에 나오는 니모닉 PECS(Productor Extensions, Consumer Super)는 Java에서 와일드카드(공분산 및 위반에 대응)를 언제 사용해야 하는지를 쉽게 기억할 수 있는 방법을 제공합니다.
범용 클래스 정의
다음으로 범용 Java 클래스의 예를 나타냅니다.이 클래스는 맵에서 개개의 엔트리(키와 값의 매핑)를 나타내기 위해 사용할 수 있습니다.
일반의 학급 엔트리< >키 타입, 밸류 타입> { 사적인 최종 키 타입 열쇠; 사적인 최종 밸류 타입 가치; 일반의 엔트리(키 타입 열쇠, 밸류 타입 가치) { 이것..열쇠 = 열쇠; 이것..가치 = 가치; } 일반의 키 타입 키() { 돌아가다 열쇠; } 일반의 밸류 타입 가치의 취득() { 돌아가다 가치; } 일반의 스트링 문자열() { 돌아가다 "(" + 열쇠 + ", " + 가치 + ")"; } }
이 범용 클래스는 다음과 같은 방법으로 사용할 수 있습니다.
엔트리< >스트링, 스트링> 등급. = 신규 엔트리< >스트링, 스트링>('마이크, "A"); 엔트리< >스트링, 정수> 마크. = 신규 엔트리< >스트링, 정수>('마이크, 100); 시스템..나가..인쇄("등급: " + 등급.); 시스템..나가..인쇄("마크: " + 마크.); 엔트리< >정수, 부울> 프라임 = 신규 엔트리< >정수, 부울>(13, 진실의); 한다면 (프라임.가치의 취득()) 시스템..나가..인쇄(프라임.키() + "는 소수입니다.); 또 다른 시스템..나가..인쇄(프라임.키() + "는 소수가 아닙니다.);
출력:
등급: (마이크, A) 마크: (마이크, 100) 13은 소수입니다.
다이아몬드 연산자
타입 추론 덕분에 Java SE 7 이상에서는 프로그래머가 빈 앵글 괄호 쌍을 대체할 수 있습니다.<>
(다이아몬드 연산자)는 충분히 가까운 컨텍스트가 암시하는 [8]하나 이상의 유형 파라미터를 포함하는 한 쌍의 각도 괄호에서 사용됩니다.따라서 위의 코드 예는 다음과 같습니다.Entry
다음과 같이 고쳐 쓸 수 있습니다.
엔트리< >스트링, 스트링> 등급. = 신규 엔트리<< 고객명 >>님('마이크, "A"); 엔트리< >스트링, 정수> 마크. = 신규 엔트리<< 고객명 >>님('마이크, 100); 시스템..나가..인쇄("등급: " + 등급.); 시스템..나가..인쇄("마크: " + 마크.); 엔트리< >정수, 부울> 프라임 = 신규 엔트리<< 고객명 >>님(13, 진실의); 한다면 (프라임.가치의 취득()) 시스템..나가..인쇄(프라임.키() + "는 소수입니다.); 또 다른 시스템..나가..인쇄(프라임.키() + "는 소수가 아닙니다.);
일반 메서드의 정의
위의 범용 클래스를 사용하는 범용 메서드의 예를 다음에 나타냅니다.
일반의 정적인 < >유형> 엔트리< >유형, 유형> 두번이라.(유형 가치) { 돌아가다 신규 엔트리< >유형, 유형>(가치, 가치); }
주의: 첫 번째 파일을 삭제한 경우<Type>
위 방법에서는 기호 선언을 나타내므로 컴파일 오류('Type'을 찾을 수 없음)가 발생합니다.
대부분의 경우 메서드 사용자는 다음과 같이 추측할 수 있으므로 유형 파라미터를 지정할 필요가 없습니다.
엔트리< >스트링, 스트링> 짝 = 엔트리.두번이라.("안녕하세요");
필요에 따라 파라미터를 명시적으로 추가할 수 있습니다.
엔트리< >스트링, 스트링> 짝 = 엔트리.< >스트링>두번이라.("안녕하세요");
원시 유형은 사용할 수 없습니다. 대신 박스 버전을 사용해야 합니다.
엔트리< >인트, 인트> 짝; // 컴파일에 실패했습니다.대신 Integer를 사용합니다.
또한 지정된 매개 변수를 기반으로 일반 메서드를 생성할 수도 있습니다.
일반의 < >유형> 유형[] 어레이(유형... 요소들) { 돌아가다 요소들; }
이 경우 다음과 같은 원시 유형도 사용할 수 없습니다.
정수[] 배열 = 어레이(1, 2, 3, 4, 5, 6);
총칭 in throws 절
예외 자체는 범용일 수 없지만 범용 파라미터는 throws 구에 표시할 수 있습니다.
일반의 < >T 확장 던질 수 있다> 무효 스로우미컨디셔널(부울 조건부, T 예외.) 던지다 T { 한다면 (조건부) { 던지다 예외.; } }
유형 삭제 문제
제네릭은 컴파일 시에 타입이 올바른지 여부를 확인합니다.그런 다음 일반 유형 정보는 유형 삭제라고 하는 프로세스에서 제거됩니다.예를들면,List<Integer>
non-timeout 타입으로 변환됩니다.List
일반적으로 임의의 오브젝트가 포함됩니다.컴파일 시간 체크를 통해 결과 코드가 올바른 유형임을 보증합니다.
유형 삭제로 인해 런타임에 유형 매개 변수를 확인할 수 없습니다.예를 들어 다음과 같은 경우ArrayList
실행 시 검사됩니다.유형 삭제 전, 이 타입이 에러인지 아닌지를 판별하는 일반적인 방법은 없습니다.ArrayList<Integer>
또는ArrayList<Float>
많은 사람들이 이 [9]규제에 불만을 가지고 있다.부분적인 접근법이 있습니다.예를 들어, 개별 요소를 검사하여 해당 요소가 속한 유형을 결정할 수 있습니다. 예를 들어, 다음과 같은 경우ArrayList
를 포함합니다.Integer
ArrayList가 파라미터화되어 있을 가능성이 있습니다.Integer
(단, 다음 중 하나의 부모에 의해 파라미터화되었을 가능성이 있습니다).Integer
,예를 들어Number
또는Object
).
이 점을 나타내듯이 다음 코드는 "동일"을 출력합니다.
어레이 리스트< >정수> 리 = 신규 어레이 리스트< >정수>(); 어레이 리스트< >흘러가다> 만약 = 신규 어레이 리스트< >흘러가다>(); 한다면 (리.클래스 취득() == 만약.클래스 취득()) { // true로 평가 시스템..나가..인쇄('동일하다'); }
형식 삭제의 또 다른 효과는 일반 클래스가 직접 또는 [10]간접적으로 어떤 방식으로도 슬로우 가능 클래스를 확장할 수 없다는 것입니다.
일반의 학급 Generic Exception(일반 예외)< >T> 확장 예외.
이것이 지원되지 않는 이유는 다음과 같습니다.
해라 { 던지다 신규 Generic Exception(일반 예외)< >정수>(); } 또 만나(Generic Exception(일반 예외)< >정수> e) { 시스템..에러.인쇄("정수"); } 또 만나(Generic Exception(일반 예외)< >스트링> e) { 시스템..에러.인쇄('스트링'); }
유형 삭제로 인해 실행 시 어떤 캐치 블록을 실행해야 하는지 알 수 없으므로 컴파일러는 이를 금지합니다.
Java 범용은 C++ 템플릿과 다릅니다.Java 제네릭은 사용된 매개 변수화 유형의 수에 관계없이 일반 클래스 또는 함수의 컴파일된 버전을 하나만 생성합니다.또한 Java 런타임 환경은 유형 정보가 컴파일 시 검증되며 컴파일된 코드에 포함되지 않으므로 어떤 파라미터화된 유형이 사용되는지 알 필요가 없습니다.따라서 파라미터화된 유형의 Java 클래스를 인스턴스화하는 것은 불가능하며, 인스턴스화에는 생성자에 대한 호출이 필요하기 때문에 유형을 알 수 없는 경우에는 사용할 수 없습니다.
예를 들어 다음 코드를 컴파일할 수 없습니다.
< >T> T 인스턴스화 요소유형(목록.< >T> arg) { 돌아가다 신규 T(); //컴파일 오류 표시 }
런타임에는 범용 클래스당 복사본이 1개뿐이므로 정적 변수는 유형 파라미터에 관계없이 클래스의 모든 인스턴스에서 공유됩니다.따라서 type 파라미터는 정적 변수 선언이나 정적 메서드에서는 사용할 수 없습니다.
범용 프로젝터
Project Valhalla는 Java 10 이후의 향후 버전을 위해 향상된 Java 제네릭스와 언어 기능을 배양하기 위한 실험 프로젝트입니다.다음과 [11]같은 기능이 향상될 수 있습니다.
「 」를 참조해 주세요.
레퍼런스
- ^ 자바 프로그래밍 언어
- ^ ClassCastException은 캐스트 또는 늘이 없는 경우에도 느려질 수 있습니다."Java and Scala's Type Systems are Unsound" (PDF).
- ^ 범용 자바
- ^ Java Language Specification, 제3판 James Gosling, Bill Joy, Guy Stele, Gilad Bracha – 프렌티스 홀 PTR 2005
- ^ Gilad Bracha (July 5, 2004). "Generics in the Java Programming Language" (PDF). www.oracle.com.
- ^ Gilad Bracha (July 5, 2004). "Generics in the Java Programming Language" (PDF). www.oracle.com. p. 5.
- ^ Bracha, Gilad. "Wildcards > Bonus > Generics". The Java™ Tutorials. Oracle.
...The sole exception is null, which is a member of every type...
- ^ "Type Inference for Generic Instance Creation".
- ^ Gafter, Neal (2006-11-05). "Reified Generics for Java". Retrieved 2010-04-20.
- ^ "Java Language Specification, Section 8.1.2". Oracle. Retrieved 24 October 2015.
- ^ Goetz, Brian. "Welcome to Valhalla!". OpenJDK mail archive. OpenJDK. Retrieved 12 August 2014.