1. 제네릭의 개념을 제안합니다 (왜 제네릭이 필요한가)?
먼저 다음 짧은 코드를 살펴 보겠습니다.
public class genericictest {public static void main (String [] args) {list list = new arraylist (); list.add ( "qqyumidi"); list.add ( "옥수수"); list.add (100); for (int i = 0; i <list.size (); i ++) {String name = (string) list.get (i); // 1 System.out.println ( "이름 :" + 이름); }}}목록 유형 모음을 정의하고 먼저 두 개의 문자열 유형 값을 추가 한 다음 정수 유형 값을 추가하십시오. 현재 기본 유형의 목록이 객체이기 때문에 완전히 허용됩니다. 후속 루프에서는 이전 목록에 정수 유형 값 또는 기타 인코딩 이유를 추가하는 것을 잊었 기 때문에 // 1과 비슷한 오류가 쉽습니다. 컴파일 단계는 정상이므로 "java.lang.classcastException"예외는 런타임에 나타납니다. 따라서 이러한 오류는 인코딩 프로세스 중에 감지하기가 어렵습니다.
위와 같은 인코딩 과정에서 두 가지 주요 문제가 있음을 발견했습니다.
1. 컬렉션에 객체를 넣으면 컬렉션은이 객체의 유형을 기억하지 못합니다. 이 객체가 컬렉션에서 다시 꺼지면 변경된 객체의 컴파일 유형이 객체 유형이되지만 런타임 유형은 여전히 자체 유형입니다.
2. 따라서 // 1에서 수집 요소를 꺼낼 때 인위적으로 강제 유형을 특정 대상 유형으로 변환해야하며 "java.lang.classcastException"예외를 쉽게 볼 수 있습니다.
컬렉션이 컬렉션에서 다양한 유형의 요소를 기억하도록하는 방법이 있습니까? 컴파일 중에 문제가없는 한 런타임 중에 "java.lang.classcastException"예외가 없을 것입니까? 대답은 제네릭을 사용하는 것입니다.
2. 일반적인 것은 무엇입니까?
제네릭, 즉 "매개 변수 유형". 매개 변수와 관련하여 가장 친숙한 것은 메소드를 정의 할 때 구체적인 매개 변수를 갖고이 메소드를 호출 할 때 실제 매개 변수를 전달하는 것입니다. 그렇다면 매개 변수화 유형을 어떻게 이해합니까? 이름에서 알 수 있듯이 메소드의 변수 매개 변수와 유사한 원래 특정 유형에서 유형을 매개 변수화하는 것을 의미합니다. 이 시점에서 유형은 파라미터 양식 (유형 형식 매개 변수라고 할 수 있음)으로 정의 된 다음 사용/호출시 특정 유형 (유형 유형 매개 변수)이 전달됩니다.
조금 복잡해 보입니다. 먼저, 일반적인 글을 사용하여 위의 예를 살펴 보겠습니다.
공개 클래스 Generatestest {public static void main (String [] args) { /* list = new arrayList (); list.add ( "qqyumidi"); list.add ( "옥수수"); list.add (100); */ list <string> list = new ArrayList <string> (); list.add ( "qqyumidi"); list.add ( "옥수수"); //list.add(100); // 1 (int i = 0; i <list.size (); i ++) {문자열 이름 = list.get (i); // 2 System.out.println ( "이름 :" + 이름); }}}일반적인 쓰기를 사용한 후 // 1에서 정수 유형 객체를 추가하려는 경우 컴파일 오류가 발생합니다. List <string>을 통해 문자열 유형의 요소 만 목록 컬렉션에 포함될 수있는 것으로 직접 제한되어 있으므로 // 2에서 유형을 캐스트 할 필요가 없습니다. 현재 컬렉션은 요소의 유형 정보를 기억할 수 있고 컴파일러는 문자열 유형인지 확인할 수 있기 때문입니다.
위의 일반적인 정의를 결합하여 List <string>에서 String은 유형 매개 변수라는 것을 알고 있습니다. 즉, 해당 목록 인터페이스에는 유형 공식 매개 변수가 포함되어야합니다. 또한 get () 메소드의 리턴 결과는 직접이 공식 매개 변수 유형입니다 (즉, 해당 수신 유형 매개 변수). 목록 인터페이스의 특정 정의를 살펴 보겠습니다.
공개 인터페이스 목록 <e> 확장 <e> {int size (); 부울 isempty (); 부울은 (대상 O); 반복자 <e> iterator (); Object [] toArray (); <t> t [] toArray (t [] a); 부울 추가 (e e); 부울 제거 (Object O); 부울 (collection <?> c); 부울 addall (collection <? extends e> c); 부울 addall (int index, collection <? extends e> c); 부울 removeall (collection <?> c); 부울 retainall (collection <?> c); void clear (); 부울 평등 (Object O); int hashcode (); e get (int index); e set (int index, e 요소); void add (int index, e emelt); e 제거 (int index); int indexof (Object O); int lastIndexof (Object O); Listiterator <e> listiterator (); Listiterator <e> listiterator (int index); List <E> Subrist (int fromIndex, int toIndex);}We can see that after the generic definition is adopted in the List interface, E in <E> represents a type formal parameter, which can receive specific type parameters. 이 인터페이스 정의에서 E가 나타나는 경우 외부에서 허용되는 동일한 유형 매개 변수가 허용됨을 의미합니다.
당연히 ArrayList는 목록 인터페이스의 구현 클래스이며 해당 정의 양식은 다음과 같습니다.
이것으로부터 소스 코드 관점에서 정수 유형 객체가 // 1에서 잘못 컴파일되는 이유를 이해하고 // 2에서 얻은 유형은 직접 문자열 유형입니다.
Public Class ArrayList <e>는 AbstractList <e>를 확장합니다. List <e>, RandomAccess, Clonable, Serializable {public boolean add (e e) {ensurecapacity internal (size + 1); // MODCOUNT를 증가시킵니다 !! ElementData [size ++] = e; 진실을 반환하십시오. } public e get (int index) {rangecheck (index); CheckforComodification (); RETURN arrayList.this.elementData (오프셋 + 색인); } //...omit 기타 특정 정의 프로세스}3. 일반 인터페이스, 일반 클래스 및 일반 방법을 사용자 정의하십시오
위의 내용에서 모든 사람은 제네릭의 특정 작동 프로세스를 이해했습니다. 인터페이스, 클래스 및 메소드는 제네릭을 사용하여 정의하고 그에 따라 사용될 수도 있습니다. 예, 구체적으로 사용하면 일반 인터페이스, 일반 클래스 및 일반 방법으로 나눌 수 있습니다.
사용자 정의 일반 인터페이스, 일반 클래스 및 일반 메소드는 위의 Java 소스 코드의 목록 및 배열 목록과 유사합니다. 다음과 같이, 우리는 가장 간단한 일반 클래스 및 메소드 정의를 살펴 봅니다.
공개 클래스 생성 {public static void main (string [] args) {box <string> name = new Box <string> ( "옥수수"); System.out.println ( "이름 :" + name.getData ()); }} 클래스 박스 <t> {private t data; public box () {} public box (t data) {this.data = data; } public t getData () {반환 데이터; }}일반 인터페이스, 일반 클래스 및 일반 방법을 정의하는 과정에서 T, E, K, V 등과 같은 일반적인 매개 변수는 외부 사용에서 전달되는 유형 매개 변수를 수신하기 때문에 종종 일반적인 공식 매개 변수를 나타내는 데 사용됩니다. 따라서 다른 유형의 들어오는 매개 변수의 경우 해당 객체 인스턴스의 유형이 동일하게 생성됩니까?
공개 클래스 생성 {public static void main (string [] args) {box <string> name = new Box <string> ( "옥수수"); Box <integer> age = new Box <integer> (712); System.out.println ( "이름 클래스 :" + name.getClass ()); // com.qqyumidi.box system.out.println ( "Age Class :" + age.getClass ()); // com.qqyumidi.box system.out.println (name.getClass () == age.getClass ()); // 진실 }}이것으로부터, 우리는 일반 클래스를 사용할 때 다른 일반적인 인수가 전달되지만 다른 유형이 진정한 의미에서 생성되지 않는다는 것을 발견했습니다. 메모리에 다른 일반적인 인수를 전달하는 일반적인 클래스는 하나뿐입니다. 즉, 여전히 가장 기본적인 유형입니다 (이 예에서는 상자). 물론 논리적으로 여러 가지 일반적인 유형으로 이해할 수 있습니다.
그 이유는 Java에서 제네릭 개념의 목적이 코드 컴파일 단계에서만 작동하기 때문입니다. 편집 과정에서 제네릭 결과를 올바르게 확인한 후 제네릭의 관련 정보가 지워집니다. 즉, 성공적으로 컴파일 된 클래스 파일에는 일반 정보가 포함되어 있지 않습니다. 일반 정보는 런타임 단계에 들어 가지 않습니다.
이것은 한 문장으로 요약됩니다. 일반 유형은 논리적으로 여러 가지 다른 유형으로 간주되며 실제로 동일한 기본 유형입니다.
네. 와일드 카드를 입력하십시오
위의 결론에 따라 Box <bumer> 및 Box <Integer>는 실제로 두 상자 유형이라는 것을 알고 있습니다. 이제 우리는 계속 질문을 탐색해야합니다. 그렇다면 논리적으로 상자 <번호> 및 상자 <integer>를 부모-자녀 관계와 일반 유형으로 간주 할 수 있습니까?
이 문제를 명확히하기 위해 다음 예를 계속 살펴 보겠습니다.
공개 클래스 생성 {public static void main (String [] args) {box <number> name = new Box <number> (99); Box <integer> age = new Box <integer> (712); getData (이름); // 유형의 getData (box <number>) 메소드는 getData (age)에 인수에 적용 할 수 없습니다. // 1} public static void getData (box <number> data) {system.out.println ( "data :" + data.getData ()); }}코드 // 1 : t ype generictest의 메소드 getData (box <number>)에 오류 메시지가 표시되는 것을 발견했습니다 (box <integer>). 분명히, 정보를 제시함으로써, 우리는 Box <bumer>가 논리적으로 상자의 상자 클래스 <integer>로 간주 될 수 없다는 것을 알고 있습니다. 그렇다면 이유는 무엇입니까?
공개 클래스 생성 {public static void main (String [] args) {box <integer> a = new Box <integer> (712); Box <number> b = a; // 1 Box <Float> f = 새 상자 <Float> (3.14f); B. 세트 데이터 (F); // 2} public static void getData (box <number> data) {system.out.println ( "data :" + data.getData ()); }} 클래스 박스 <t> {private t data; public box () {} public box (t data) {setData (data); } public t getData () {반환 데이터; } public void setData (t data) {this.data = data; }}이 예에서는 // 1 및 // 2에 오류 메시지가 있습니다. 여기서 우리는 반격 방법을 사용하여 설명 할 수 있습니다.
상자 <번호>가 논리적으로 상자 <integer>의 상위 클래스로 간주 될 수 있다고 가정하면 // 1 및 // 2에서 오류 프롬프트가 없습니다. 그런 다음 문제가 발생합니다. getData () 메소드를 통해 데이터를 가져올 때 어떤 유형입니까? 정수? 뜨다? 또는 숫자? 또한 프로그래밍 프로세스에서 통제 할 수없는 순서로 인해 필요할 때 유형 판단이 이루어져야하며 유형 변환이 수행됩니다. 분명히 이것은 제네릭에 대한 아이디어와 모순되므로 논리적으로 Box <번호>는 상자의 상자 <integer>의 부모 클래스로 간주 될 수 없습니다.
자, "타입 와일드 카드"의 첫 번째 예를 되돌아 보자. 특정 오류 프롬프트에 대한 더 깊은 이유를 알고있다. 그래서 그것을 해결하는 방법? 본사는 새로운 기능을 정의 할 수 있습니다. 이는 Java의 다형성 개념과는 반대로 상반되므로 Box <Integer> 및 Box <bumer>의 부모 클래스를 표현하는 데 논리적으로 사용할 수있는 참조 유형이 필요하므로 WildCard 유형이 생겼습니다.
타입 와일드 카드는 일반적으로 특정 유형 인수 대신 사용됩니다. 이것은 유형 매개 변수가 아닌 유형 매개 변수입니다! 그리고 Box <?>는 논리적으로 모든 상자 <integer>, box <number> ... 등의 부모 클래스입니다. 따라서 우리는 여전히 그러한 요구 사항을 충족시키기 위해 일반적인 방법을 정의 할 수 있습니다.
공개 클래스 생성 {public static void main (string [] args) {box <string> name = new Box <string> ( "옥수수"); Box <integer> age = new Box <integer> (712); Box <number> 번호 = New Box <number> (314); getData (이름); getData (나이); getData (번호); } public static void getData (box <?> data) {System.out.println ( "data :" + data.getData ()); }}때때로, 우리는 또한 하위 유형의 와일드 카드에 대해들을 수도 있습니다. 정확히 어떤가요?
위의 예에서는 getData ()와 유사한 기능을하는 메소드를 정의해야하지만 유형 인수에 대한 추가 제한이 있습니다. 숫자 클래스와 하위 클래스 일 수 있습니다. 현재 타입 와일드 카드의 상한이 필요합니다.
공개 클래스 생성 {public static void main (string [] args) {box <string> name = new Box <string> ( "옥수수"); Box <integer> age = new Box <integer> (712); Box <number> 번호 = New Box <number> (314); getData (이름); getData (나이); getData (번호); // getUpperNumberData (이름); // 1 getUpperNumberData (나이); // 2 getUpperNumberData (번호); // 3} public static void getData (box <?> data) {system.out.println ( "data :" + data.getData ()); } public static void getUpperNumberData (box <? extrends number> data) {system.out.println ( "data :" + data.getData ()); }}이 시점에서 Code // 1의 호출은 오류 메시지가 나타나고 // 2 // 3의 호출은 정상입니다.
타입 와일드 카드의 상한은 박스 형태로 정의됩니다. 숫자>를 확장합니다. 이에 따라 타입 와일드 카드의 하한은 상자의 형태 <? 수퍼 번호> 및 그 의미는 타입 와일드 카드의 상한과 정확히 반대입니다. 나는 여기서 너무 많이 설명하지 않을 것입니다.
5. 추가 장
이 기사의 예는 주로 제네릭의 아이디어를 설명하기 위해 주로 인용되며 반드시 실질적인 유용성을 갖는 것은 아닙니다. 또한 제네릭과 관련하여 가장 많이 사용하는 것은 컬렉션에 있다고 생각합니다. 실제로 실제 프로그래밍 프로세스에서 제네릭을 사용하여 개발을 단순화하고 코드의 품질을 잘 보장 할 수 있습니다. 그리고 주목해야 할 것은 Java에는 소위 일반적인 배열이 없다는 것입니다.
제네릭의 경우 가장 중요한 것은 그 뒤에있는 아이디어와 목적을 이해하는 것입니다.
위의 것은 Java 제네릭에 대한 지식과 정보의 편집입니다. 우리는 향후 관련 정보를 계속 추가 할 것입니다. 이 웹 사이트를 지원 해주셔서 감사합니다!