Java 범용 요약 및 상세 정보

11455 단어 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 집합에 String 형식의 요소만 포함하도록 직접 한정하여/2곳에서 강제 형식 변환을 할 필요가 없습니다. 이때 집합은 요소의 유형 정보를 기억할 수 있기 때문에 컴파일러는 String 형식임을 확인할 수 있습니다.
위의 범용 정의와 결합하여 우리는 List에서 String이 유형 실삼이라는 것을 알고 있다. 즉, 상응하는 List 인터페이스에 유형 실삼이 포함되어 있을 것이다.또한 get () 방법의 반환 결과는 바로 이 인삼 형식 (즉 대응하는 전입 형식 실삼) 이다.다음은 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 인터페이스에서 범형화된 정의를 사용한 후에 의 E는 유형 인삼을 나타내고 구체적인 유형 인삼을 받아들일 수 있으며 이 인터페이스 정의에서 E가 나타나는 곳은 모두 외부의 유형 인삼을 똑같이 받아들일 수 있음을 알 수 있다.
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와 Box가 사실상 모두 Box 유형이라는 것을 알고 있다. 현재 계속해서 문제를 연구해야 한다. 그러면 논리적으로 Box와 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) in the t ypeGenericTest is not applicable for the arguments (Box).분명히 알림 메시지를 통해 우리는 Box가 논리적으로 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의 부류로 볼 수 있다고 가정하면//1과/2에서 오류 알림이 없을 것입니다. 그러면 문제가 발생합니다. getData() 방법으로 데이터를 추출할 때 도대체 어떤 유형입니까?Integer? Float? 아니면 번호?또한 프로그래밍 과정에서 순서를 제어할 수 없기 때문에 필요할 때 유형 판단을 하고 강제 유형 전환을 해야 한다.분명히 이것은 범용적인 이념과 모순되기 때문에 논리적으로 Box는 Box의 부류로 볼 수 없다.
자, 그럼 고개를 돌려'유형 어댑터'의 첫 번째 예를 계속 봅시다. 우리는 그 구체적인 오류 제시의 깊은 원인을 알고 있습니다.그럼 어떻게 해결할까요?본부에서 새로운 함수를 하나 더 정의할 수 있겠지.이것은 자바의 다태적 이념과 분명히 위배되기 때문에 논리적으로 Box와 Box의 부류를 동시에 나타내는 인용 유형이 필요하다. 이로써 유형 어댑터가 생겨난다.
유형 와일드카드는 일반적으로 사용됩니까?구체적인 유형의 실삼을 대체하다.주의해라, 여기는 유형실삼이지 유형형삼이 아니다!또한 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.외편
본고의 예는 주로 범형 중의 일부 사상을 논술하기 위해 간단하게 제시한 것이지 반드시 실제적인 가용성이 있는 것은 아니다.또한 범형을 언급하면 여러분이 가장 많이 사용하는 것은 집합에 있다고 믿습니다. 사실 실제 프로그래밍 과정에서 범형을 사용하여 개발을 간소화하고 코드의 질을 잘 보장할 수 있습니다.그리고 주의해야 할 점은 자바에 이른바 범형수조설이 없다는 것이다.
범형에 대해 가장 중요한 것은 그 배후의 사상과 목적을 이해해야 한다.
이상은 자바 범주에 대한 지식 자료를 정리하고 관련 자료를 계속 보충하는 것입니다. 본 사이트에 대한 지지에 감사드립니다!

좋은 웹페이지 즐겨찾기