1. 왜 - 일반적인 메커니즘을 도입하는 이유
문자열 배열을 구현하고 크기를 동적으로 변경 해야하는 경우 ArrayList를 사용하여 문자열 객체를 집계하는 것을 모두 생각합니다. 그러나 잠시 후 크기를 변경할 수있는 일의 배열 객체를 구현하려고합니다. 현재, 우리는 이전에 쓴 문자열 객체에 대한 ArrayList 구현을 재사용 할 수 있기를 바랍니다.
Java 5 이전에 Arraylist의 구현은 다음과 같습니다.
public class arraylist {public object get (int i) {... ...} public void add (Object O) {...} ... private object [] elementData;}위의 코드에서 ADD 기능은 ArrayList에 요소를 추가하는 데 사용되는 추가 기능이 객체 유형 매개 변수를 수신 함을 알 수 있습니다. ArrayList에서 지정된 요소를 얻는 GET 메소드는 객체 유형 객체를 반환합니다. 객체 객체 배열 요소 데이터는 arraylist에 객체를 저장합니다. 즉, Arraylist에 어떤 유형의 유형을 넣더라도 그 안에있는 객체 객체입니다.
상속을 기반으로하는 일반 구현에는 두 가지 문제가 발생합니다. 첫 번째 질문은 GET 메소드에 관한 것입니다. 우리가 get 메소드를 호출 할 때마다, 우리는 객체 객체를 반환하고,마다 유형을 필요한 유형에 시전해야 할 때마다 매우 번거로운 것처럼 보일 것입니다. 두 번째 질문은 추가 방법에 관한 것입니다. String 객체를 집계하는 Arraylist에 파일 개체를 추가하면 컴파일러는 우리가 원하는 것이 아닌 오류 프롬프트를 생성하지 않습니다.
따라서 Java 5에서 시작하여 ArrayList를 사용하여 사용할 때 유형 매개 변수 (유형 매개 변수)를 추가 할 수 있습니다. 이 유형 매개 변수는 ArrayList의 요소 유형을 표시하는 데 사용됩니다. 유형 매개 변수의 도입은 다음 코드와 같이 위에서 언급 한 두 가지 문제를 해결합니다.
ArrayList <String> S = New ArrayList <string> (); S.Add ( "ABC"); String S = S.Get (0); // s.add (123)를 캐스트 할 필요가 없습니다. // 컴파일 오류, 문자열 객체 만 추가 할 수 있습니다 ...
위의 코드에서 컴파일러가 ArrayList의 유형 매개 변수 문자열을 "알고있는"후에 캐스팅 및 유형 확인을 완료합니다.
2. 일반 클래스
소위 일반 클래스는 하나 이상의 유형 매개 변수가있는 클래스입니다. 예를 들어:
공개 클래스 쌍 <t, u> {private t first; 개인 U Second; 공개 쌍 (t first, u second) {this.first = 첫 번째; this.second = 두 번째; } public t getFirst () {return first; } public u getsecond () {return second; } public void setfirst (t newValue) {first = newValue; }}위의 코드에서는 일반 클래스 쌍의 유형 매개 변수가 t와 u이며 클래스 이름 후에 각도 브래킷에 배치된다는 것을 알 수 있습니다. 여기서 t는 유형을 나타내는 첫 번째 유형을 의미합니다. 일반적으로 사용되는 것은 e (요소), k (키), V (값) 등입니다. 물론, 유형 매개 변수를 참조하기 위해이 문자를 사용하지 않는 것도 완벽합니다.
일반 클래스를 인스턴스화 할 때 유형 매개 변수를 쌍 <t, u> 클래스 인스턴스화와 같은 특정 유형으로만 바꾸면됩니다.
Pair <String, Integer> pair = new Pair <String, Integer> ();
3. 일반적인 방법
소위 일반 방법은 유형 매개 변수가있는 메소드입니다. 일반 클래스 또는 일반 클래스로 정의 할 수 있습니다. 예를 들어:
public class arrayalg {public static <t> t getMiddle (t [] a) {return a [a.length / 2]; }}위 코드의 getMiddle 메소드는 일반적인 메소드이며 정의 된 형식은 유형 변수가 수정 자 다음과 리턴 유형 이전에 배치된다는 것입니다. 우리는 다양한 유형의 배열에 대해 위의 일반적인 방법을 호출 할 수 있음을 알 수 있습니다. 이 배열의 유형이 제한된 것으로 알려져 있지만 과부하로도 구현 될 수 있지만 인코딩 효율은 훨씬 낮습니다. 위의 일반 방법을 호출하기위한 예제 코드는 다음과 같습니다.
문자열 [] strings = { "aa", "bb", "cc"};
문자열 middle = arrayalg.getMiddle (이름);
4. 유형 변수의 제한
경우에 따라 일반 클래스 또는 일반 방법이 유형 매개 변수를 더 제한하려고합니다. 예를 들어, 특정 클래스의 서브 클래스 만 있거나 특정 인터페이스를 구현하는 클래스만이 될 수있는 유형 매개 변수를 정의하려는 경우. 관련 구문은 다음과 같습니다.
<t extends boundingType> (boundingType는 클래스 또는 인터페이스입니다). 1 개 이상의 BoundingType가있을 수 있습니다. "&"만 사용하여 연결하십시오.
5. 제네릭의 구현을 이해하십시오
실제로, 가상 머신의 관점에서 볼 때 "제네릭"이라는 개념은 없습니다. 예를 들어, 위에서 정의한 일반 클래스 쌍은 가상 머신에서 이와 같이 보입니다 (즉, 바이트 코드로 컴파일 된 후).
공개 클래스 쌍 {개인 객체 먼저; 개인 대상 두 번째; public 쌍 (Object First, Object Second) {this.first = first; this.second = 두 번째; } public Object getFirst () {return first; } public object getSecond () {return Second; } public void setFirst (Object NewValue) {First = NewValue; } public void setsecond (Object NewValue) {second = newValue; }}위의 클래스는 유형 지우기에 의해 얻어지며 쌍 제네릭 클래스에 해당하는 원시 유형입니다. 유형 지우기는 모든 유형 매개 변수를 BondingType로 바꾸는 것을 의미합니다 (제한이 추가되지 않으면 개체로 교체).
쌍을 컴파일 한 후 "javap -c -s 쌍"을 입력하여 다음을 얻을 수 있습니다.
위 그림에서 "설명 자"가있는 선은 해당 방법의 서명입니다. 예를 들어, 네 번째 줄에서 쌍 생성자의 두 공식 매개 변수가 유형 지우기 후 객체가되었음을 알 수 있습니다.
일반 클래스 쌍은 가상 머신에서 원시 유형이되므로 GetFirst 메소드는 객체 객체를 반환하고 컴파일러의 관점 에서이 메소드는 클래스를 인스턴스화 할 때 지정된 유형 매개 변수의 객체를 리턴합니다. 실제로 캐스팅 작업을 완료하는 데 도움이되는 컴파일러입니다. 다시 말해, 컴파일러는 통화를 쌍 일반 클래스의 GetFirst 메소드로 변환합니다.
첫 번째는 객체 객체를 반환하는 RAW 유형 메소드 GetFirst에 대한 호출입니다. 두 번째 명령어는 반환 된 객체 객체를 우리가 지정한 유형 매개 변수 유형으로 시전합니다.
유형 삭제는 다음과 같은 일반적인 방법과 같은 일반적인 방법에서도 발생합니다.
public static <t는 비슷한> t min (t [] a)를 확장합니다.
편집 후 유형 지우기 후 다음과 같이됩니다.
공개 정적 비교 최소 (비슷한 [] a)
메소드의 유형 지우기는 몇 가지 문제를 일으킬 수 있으며 다음 코드를 고려하십시오.
클래스 DateInterval은 쌍 <날짜, 날짜> {public void setsecond (date sec }} ...}위의 코드가 유형별로 지워지면 다음이됩니다.
클래스 DateInterval은 쌍 {public void setsecond (날짜) {...} ...}을 확장합니다.DateInterval 클래스에는 다음과 같이 쌍 클래스 (유형 지우기 후)에서 상속 된 세트 메소드도 있습니다.
공개 void setsecond (객체 2 위)
이제이 방법에는 dateinterval에 의해 재정의 된 세트 초 모부들과 다른 메소드 서명 (다른 공식 매개 변수)이 있기 때문에 두 가지 다른 방법이지만이 두 가지 메소드는 다른 메소드가되어서는 안됩니다 (재정의이기 때문에). 다음 코드를 고려하십시오.
dateinterval interval = new dateinterval (...); pair <날짜, 날짜> 쌍 = 간격; 날짜 adate = new 날짜 (...); pair.setsecond (adate);
위의 코드에서 쌍은 실제로 DateInterval 객체를 참조하므로 SetSecond Method of Date Interval이 호출되어야한다는 것을 알 수 있습니다. 여기서 문제는 유형을 지우는 유형이 다형성과 충돌하는 것입니다.
이 문제가 발생하는 이유를 분류 해 봅시다. 쌍은 이전에 쌍 <날짜, 날짜> 유형으로 선언 되었으며이 클래스에는 가상 시스템에 하나의 "setsecond (객체)"메소드가있는 것 같습니다. 따라서 실행할 때 가상 머신은 쌍이 실제로 DateInterval 객체를 참조하고 DateInterval의 "setsecond (object)"를 호출하지만 dateinterval 클래스에는 "setsecond (date)"메소드 만 있습니다.
이 문제에 대한 해결책은 컴파일러가 Date Interval에서 브리지 방법을 생성하는 것입니다.
public void setsecond (객체 두 번째) {setsecond ((날짜) second);}6. 주목할만한 것들
(1) 유형 매개 변수는 기본 유형으로 인스턴스화 할 수 없습니다.
즉, 다음 진술은 불법입니다.
쌍 <int, int> pair = new Pair <int, int> ();
그러나 대신 해당 포장 유형을 사용할 수 있습니다.
(2) 일반 클래스 인스턴스를 던지거나 캡처 할 수 없습니다
일반적인 클래스 확장 프로그램은 불법이므로 일반적인 클래스 인스턴스를 던지거나 포착 할 수 없습니다. 그러나 예외 선언에서 유형 매개 변수를 사용하는 것은 합법적입니다.
public static <t throwsable> void dowork (t t)는 t {try {...} catch (Throwable RealCause) {t.initcause (realcause); 던지기; }}(3) 매개 변수 배열은 불법입니다
Java에서 객체 [] 배열은 모든 배열의 상위 클래스 일 수 있습니다 (모든 배열은 정의시 요소 유형을 지정하는 부모 클래스의 배열로 위로 변환 할 수 있기 때문에). 다음 코드를 고려하십시오.
문자열 [] strs = 새 문자열 [10]; object [] objs = strs; obj [0] = 새 날짜 (...);
위의 코드에서는 배열 요소를 부모 클래스 (개체) 유형을 만족시키는 객체에 할당하지만 원래 유형 (쌍)과 달리 컴파일 시간에 전달 될 수 있으며 ArrayStoreException 예외는 런타임에 발생합니다.
위의 이유에 따라 Java가 다음과 같은 진술을 통해 일반 배열을 선언하고 초기화 할 수 있다고 가정합니다.
pair <string, String> [] pairs = new Pair <String, String> [10];
그런 다음 가상 머신에서 유형 지우기를 수행 한 후에는 쌍이 실제로 쌍 배열이되고 객체 [] 배열로 위쪽으로 변환 할 수 있습니다. 현재 <날짜, 날짜> 개체를 추가하면 컴파일 타임 검사 및 런타임 확인을 통과 할 수 있습니다. 우리의 원래 의도는이 배열 스토어 쌍 <string, string> 객체를 놓아 두는 것입니다. 따라서 Java는 위의 명령문 양식을 통해 일반 배열을 선언하고 초기화 할 수 없습니다.
일반 배열은 다음 과정을 사용하여 선언 및 초기화 할 수 있습니다.
pair <string, string> [] pairs = (pair <string, String> []) 새 쌍 [10];
(4) 유형 변수를 인스턴스화 할 수 없습니다
유형 변수는 "new t (...)", "new t [...]", "t.class"와 같은 형태로 사용할 수 없습니다. Java 가이 작업을 수행하는 것을 금지하는 이유는 간단합니다. 유형 삭제가 있기 때문에 "new t (...)"와 같은 진술은 "새로운 대상 (...)"이 될 것입니다. 이것은 일반적으로 우리가 의미하는 것이 아닙니다. "New T [...]"로 전화를 대체 할 수 있습니다.
배열 = (t []) 새 개체 [n];
(5) 유형 변수는 일반 클래스의 정적 컨텍스트에서 사용할 수 없습니다.
우리는 여기서 일반 수업을 강조합니다. 정적 일반 방법은 위에서 언급 한 ArrayAlg 클래스의 getMiddle 메소드와 같은 일반 클래스에서 정의 될 수 있기 때문입니다. 그러한 규칙의 이유로 다음 코드를 고려하십시오.
공개 계급 사람들 <t> {공개 정적 t 이름; public static t getName () {...}}우리는 동시에 기억에 <t> 클래스 인스턴스가 둘 이상있을 수 있음을 알고 있습니다. 지금 메모리에 사람 <string> 객체와 사람의 객체가 있고, 클래스의 정적 변수와 정적 메소드가 모든 클래스 인스턴스에서 공유한다고 가정 해 봅시다. 따라서 문제는 이름 문자열 유형 또는 정수 유형입니까? 이러한 이유로 Java에서는 일반 클래스의 정적 컨텍스트에서 유형 변수가 허용되지 않습니다.
7. 와일드 카드를 입력하십시오
타입 와일드 카드를 소개하기 전에 먼저 두 가지 점을 소개합니다.
(1) 학생이 사람들의 서브 클래스라고 가정하지만, 쌍 <학생, 학생>은 쌍 <사람, 사람>의 서브 클래스가 아니며, 그들 사이에 "is-a"관계가 없습니다.
(2) 쌍 <t, t>와 그 원래 유형 쌍 사이에는 "IS-A"관계가 있습니다. 쌍 <t, t>는 어쨌든 쌍 유형으로 변환 할 수 있습니다.
이제이 방법을 고려하십시오.
public static void printname (pair <people, people> p) {people p1 = p.getfirst (); System.out.println (p1.getName ()); // People 클래스가 getName 인스턴스 메소드를 정의한다고 가정합니다}위의 방법에서는 <학생, 학생> 및 쌍 <사람, 사람>의 매개 변수를 동시에 전달할 수 있지만, 둘 사이에는 "IS-A"관계가 없습니다. 이 경우 Java는 다음과 같은 솔루션을 제공합니다. Pair <? 공식 매개 변수의 유형으로 사람들을 확장합니다. 즉, Pain <Student, Student> 및 Pair <People, People> 둘 다 쌍의 서브 클래스로 간주 될 수 있습니까? 사람들을 확장합니다>.
"<? Extends BoundingType>"과 같은 코드를 와일드 카드 문자의 하위 유형 제한이라고합니다. 이에 해당하는 것은 와일드 카드 문자의 슈퍼 유형 제한입니다. 형식은 다음과 같습니다. <? Super BoundingType>.
이제 다음 코드를 고려해 봅시다.
쌍 <학생> 학생 = New Pair <tudent> (Student1, Student2); pair <? 사람들을 확장합니다> WildChards = 학생; WildChards.setfirst (people1);
위의 코드의 세 번째 줄은 WildChards가 쌍이기 때문에 오류를보고합니다. 사람> 객체를 확장하며, 해당 메소드 및 GetFirst 메소드는 다음과 같습니다.
void setfirst (? 사람들을 확장)? 사람들이 확장 getfirst ()
SetFirst 방법의 경우 컴파일러는 공식 매개 변수의 유형 (사람의 서브 클래스로만 알려짐)을 알지 못합니다. People Object를 통과하려고 할 때 컴파일러는 사람과 공식 매개 변수가 "IS-A"인지 여부를 결정할 수 없으므로 SetFirst 메소드를 호출하면 오류가보고됩니다. WildChards의 GetFirst 방법을 호출하는 것은 합법적이며 사람들의 서브 클래스를 반환하고 사람들의 서브 클래스는 "항상 사람들입니다". (항상 서브 클래스 객체를 부모 객체로 변환 할 수 있습니다)
와일드 카드 슈퍼 타입 제한의 경우, 호출 getter 방법은 불법이며, Setter 방법을 호출하는 것은 합법적입니다.
하위 유형 제한 및 초형 제한 외에도 무한 와일드 카드라고 불리는 와일드 카드도 있습니다. <?>. 우리는 언제 이것을 사용합니까? 이 시나리오를 고려하십시오. 메소드를 호출하면 getPairs 메소드를 반환하여 <t, t> 객체 세트를 반환합니다. 그중에는 쌍 <학생, 학생> 및 페어 <교사, 교사> 대상이 있습니다. (학생 수업과 교사 수업 사이에는 상속 관계가 없습니다) 분명히,이 경우 하위 유형 제한과 초형 제한을 모두 사용할 수 없습니다. 현재이 진술을 사용하여 다음을 해결할 수 있습니다.
쌍 <?> [] pairs = getPairs (...);