Java 범용 요약 및 상세 정보
먼저 다음 간단한 코드를 살펴보겠습니다.
public class GenericTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100);
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); // 1
System.out.println("name:" + name);
}
}
}
List 유형의 컬렉션을 정의하고 두 문자열 유형의 값을 추가한 다음 Integer 유형의 값을 추가합니다.이것은 완전히 허용됩니다. 이 때 list의 기본 형식은 Object 형식이기 때문입니다.이후 순환에서 이전list에도 Integer 형식의 값이나 다른 인코딩을 추가한 것을 잊어버려서//1과 유사한 오류가 발생하기 쉽습니다.컴파일 단계가 정상적이기 때문에 실행할 때 "java.lang.ClassCastException"이상이 발생합니다.따라서 이러한 오류를 인코딩하는 과정에서 발견하기 어렵다.위의 인코딩 과정에서 우리는 주로 두 가지 문제가 존재한다는 것을 발견했다.
1. 우리가 하나의 대상을 집합에 넣으면 집합은 이 대상의 유형을 기억하지 못한다. 집합에서 이 대상을 다시 꺼낼 때, 대상의 컴파일 형식은 Object 형식으로 바뀌지만, 실행할 때 형식은 그 자체의 유형이다.
따라서//1에서 집합 요소를 추출할 때 인위적인 강제 유형이 구체적인 목표 유형으로 바뀌어야 하며'java.lang.ClassCastException'이상이 발생하기 쉽다.
그러면 집합이 집합 내 요소의 각 유형을 기억할 수 있고 컴파일할 때 문제가 발생하지 않으면 실행할 때 "java.lang.ClassCastException"이상이 발생하지 않도록 할 수 있는 방법은 없을까요?정답은 범형을 사용하는 것이다.
2.무엇이 범용입니까?
범위, 즉 매개 변수화 유형.매개 변수를 언급하면 가장 익숙한 것은 방법을 정의할 때 형삼이 있고 이 방법을 호출할 때 실삼을 전달하는 것이다.그러면 매개 변수화 유형은 어떻게 이해합니까?말 그대로 유형을 원래의 구체적인 유형에 의해 매개 변수화하는 것이다. 방법의 변수 매개 변수와 유사하다. 이때 유형도 매개 변수 형식으로 정의한 다음에 사용/호출 시 구체적인 유형(유형 실참)을 전달한다.
보기에는 좀 복잡한 것 같다. 우선 위의 그 예가 범용적인 묘사법을 채택한 것을 보자.
public class GenericTest {
public static void main(String[] args) {
/*
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100);
*/
List<String> list = new ArrayList<String>();
list.add("qqyumidi");
list.add("corn");
//list.add(100); // 1
for (int i = 0; i < list.size(); i++) {
String name = list.get(i); // 2
System.out.println("name:" + name);
}
}
}
범용 쓰기 후//1에 Integer 형식의 대상을 추가하려고 할 때 컴파일 오류가 발생합니다. List 위의 범용 정의와 결합하여 우리는 List
public interface List<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
List<E> subList(int fromIndex, int toIndex);
}
우리는 List 인터페이스에서 범형화된 정의를 사용한 후에 ArrayList는 List 인터페이스의 구현 클래스로서 다음과 같이 정의됩니다.
이로써 우리는 소스 코드의 측면에서 왜//1에 Integer 형식의 대상을 추가하여 컴파일하는 데 오류가 발생했는지, 그리고//2곳의 get()에서 오는 형식이 바로 String 형식인지 알게 되었다.
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
//...
}
셋.사용자 정의 범용 인터페이스, 범용 클래스와 범용 방법위의 내용에서 모두는 이미 범형의 구체적인 운영 과정을 이해했다.인터페이스, 클래스와 방법도 모두 범형으로 정의하고 해당하는 사용을 할 수 있다는 것을 알게 되었다.네, 구체적으로 사용할 때 범용 인터페이스, 범용 클래스와 범용 방법으로 나눌 수 있습니다.
사용자 정의 범용 인터페이스, 범용 클래스와 범용 방법은 상기 자바 원본 코드의 List, Array List와 유사하다.다음은 가장 간단한 범용 클래스와 방법 정의입니다.
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<String>("corn");
System.out.println("name:" + name.getData());
}
}
class Box<T> {
private T data;
public Box() {
}
public Box(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
범용 인터페이스, 범용 클래스와 범용 방법의 정의 과정에서 우리가 흔히 볼 수 있는 T, E, K, V 등 형식의 매개 변수는 범용 트리를 나타내는 데 사용되는데 외부에서 사용할 때 전송된 유형의 실삼을 수신하기 때문이다.그렇다면 서로 다른 전입된 유형의 실삼에 대해 생성된 상응하는 대상의 실례의 유형은 같지 않습니까?
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712);
System.out.println("name class:" + name.getClass()); // com.qqyumidi.Box
System.out.println("age class:" + age.getClass()); // com.qqyumidi.Box
System.out.println(name.getClass() == age.getClass()); // true
}
}
이로써 우리는 범형류를 사용할 때 서로 다른 범형실삼을 전입했지만 진정한 의미에서 서로 다른 유형을 생성하지 못했다. 서로 다른 범형실삼을 전입한 범형류는 메모리에 오직 하나, 즉 원래의 가장 기본적인 유형(본 실례는 Box)이었다. 물론 논리적으로 우리는 여러 개의 서로 다른 범형유형으로 이해할 수 있다.그 이유는 자바에서의 범주형이라는 개념이 제시한 목적이 코드 컴파일 단계에 작용하기 때문이다. 컴파일 과정에서 범주형 결과를 정확하게 검증한 후에 범주형의 관련 정보를 닦아낸다. 즉, 성공적으로 컴파일한 후의class 파일에는 어떠한 범주형 정보도 포함되지 않는다는 것이다.범용 정보는 실행 단계에 들어가지 않습니다.
이에 대해 한마디로 요약하면 범형 유형은 논리적으로 보면 여러 가지 다른 유형으로 보이지만 실제로는 모두 같은 기본 유형이다.
사.유형 와일드카드
이어서 위의 결론에 의하면 우리는 Box
이 문제를 분명히 하기 위해서 우리는 계속해서 아래의 이 예를 보았다.
public class GenericTest {
public static void main(String[] args) {
Box<Number> name = new Box<Number>(99);
Box<Integer> age = new Box<Integer>(712);
getData(name);
//The method getData(Box<Number>) in the type GenericTest is
//not applicable for the arguments (Box<Integer>)
getData(age); // 1
}
public static void getData(Box<Number> data){
System.out.println("data :" + data.getData());
}
}
코드//1에서 오류 알림 메시지가 발생했습니다. The method getData (Box
public class GenericTest {
public static void main(String[] args) {
Box<Integer> a = new Box<Integer>(712);
Box<Number> b = a; // 1
Box<Float> f = new Box<Float>(3.14f);
b.setData(f); // 2
}
public static void getData(Box<Number> data) {
System.out.println("data :" + data.getData());
}
}
class Box<T> {
private T data;
public Box() {
}
public Box(T data) {
setData(data);
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
이 예에서 분명히///1과/2 곳에서 틀림없이 잘못된 알림이 나타날 것이다.여기서 우리는 반증법을 사용하여 설명할 수 있다.만약 Box
자, 그럼 고개를 돌려'유형 어댑터'의 첫 번째 예를 계속 봅시다. 우리는 그 구체적인 오류 제시의 깊은 원인을 알고 있습니다.그럼 어떻게 해결할까요?본부에서 새로운 함수를 하나 더 정의할 수 있겠지.이것은 자바의 다태적 이념과 분명히 위배되기 때문에 논리적으로 Box
유형 와일드카드는 일반적으로 사용됩니까?구체적인 유형의 실삼을 대체하다.주의해라, 여기는 유형실삼이지 유형형삼이 아니다!또한 Box논리적으로는 Box
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712);
Box<Number> number = new Box<Number>(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(Box<?> data) {
System.out.println("data :" + data.getData());
}
}
때때로, 우리는 유형 어댑터 상한선과 유형 어댑터 하한선을 들을 수도 있다.구체적으로 어떤 게 있을까요?위의 예에서 getData () 와 같은 기능을 정의하는 방법이 필요하지만, 형식 실참에 대한 제한은 Number 클래스와 하위 클래스일 뿐입니다.이제 유형 와일드카드 상한선이 필요합니다.
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712);
Box<Number> number = new Box<Number>(314);
getData(name);
getData(age);
getData(number);
//getUpperNumberData(name); // 1
getUpperNumberData(age); // 2
getUpperNumberData(number); // 3
}
public static void getData(Box<?> data) {
System.out.println("data :" + data.getData());
}
public static void getUpperNumberData(Box<? extends Number> data){
System.out.println("data :" + data.getData());
}
}
이 때, 분명히 코드//1에서 호출하면 오류 알림이 발생하고,/2//3에서 정상적으로 호출됩니다.유형 와일드카드 상한선은 Box 형식 정의, 그에 상응하는 유형 어댑터 하한선은 Box 형식, 그 의미는 유형 어댑터 상한선과 정반대입니다. 여기서 너무 많이 논술하지 않겠습니다.
5.외편
본고의 예는 주로 범형 중의 일부 사상을 논술하기 위해 간단하게 제시한 것이지 반드시 실제적인 가용성이 있는 것은 아니다.또한 범형을 언급하면 여러분이 가장 많이 사용하는 것은 집합에 있다고 믿습니다. 사실 실제 프로그래밍 과정에서 범형을 사용하여 개발을 간소화하고 코드의 질을 잘 보장할 수 있습니다.그리고 주의해야 할 점은 자바에 이른바 범형수조설이 없다는 것이다.
범형에 대해 가장 중요한 것은 그 배후의 사상과 목적을 이해해야 한다.
이상은 자바 범주에 대한 지식 자료를 정리하고 관련 자료를 계속 보충하는 것입니다. 본 사이트에 대한 지지에 감사드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.