>  기사  >  Java  >  Java 제네릭의 구현 원칙

Java 제네릭의 구현 원칙

高洛峰
高洛峰원래의
2016-12-19 15:47:181413검색

1. 자바 제네릭 소개

제네릭은 Java 1.5의 새로운 기능입니다. 제네릭의 본질은 매개변수화된 유형입니다. 즉, 연산되는 데이터 유형이 매개변수로 지정됩니다. 이 매개변수 유형은 각각 일반 클래스, 일반 인터페이스 및 일반 메소드라고 하는 클래스, 인터페이스 및 메소드 작성에 사용될 수 있습니다.

이번에 소개되는 Java 제네릭의 장점은 안전하고 간편하다는 것입니다.

Java SE 1.5 이전에는 제네릭이 없을 때 "임의" 매개변수가 Object 유형을 참조하여 구현되었습니다. "임의"의 단점은 명시적인 강제 변환이 필요하다는 점이었습니다. 개발자는 실제 매개변수 유형을 미리 알아야 합니다. 강제 유형 변환 오류의 경우 컴파일러에서 오류 메시지를 표시하지 않을 수 있으며 런타임 중에 예외가 발생합니다. 이는 보안상 위험합니다.

제네릭의 장점은 컴파일 중에 유형 안전성을 확인하고 모든 캐스트가 자동으로 암시적으로 수행되어 코드 재사용이 향상된다는 것입니다.

제네릭 사용에는 몇 가지 규칙과 제한 사항이 있습니다.

1. 제네릭의 유형 매개변수는 클래스 유형(사용자 정의 클래스 포함)만 가능하며, 간단한 유형.

2. 동일한 제네릭 유형이 여러 버전에 해당할 수 있으며(매개변수 유형이 불확실하기 때문에) 제네릭 클래스 인스턴스의 다른 버전은 호환되지 않습니다.

3. 제네릭은 여러 유형 매개변수를 가질 수 있습니다.

4. 예를 들어 일반 매개변수 유형은 확장 문을 사용할 수 있습니다. 관례적으로 "제한된 유형"이 됩니다.

5. 일반 매개변수 유형은 와일드카드 유형일 수도 있습니다.

Class<?> classType = Class.forName(java.lang.String);

제네릭에도 인터페이스, 메소드 등이 있습니다. 내용이 많고, 이를 능숙하게 이해하고 적용하려면 많은 노력이 필요합니다.

2. Java 제네릭 구현 원칙: 유형 삭제

Java의 제네릭은 의사 제네릭입니다. 컴파일하는 동안 모든 일반 정보가 지워집니다. 제네릭의 개념을 올바르게 이해하기 위한 첫 번째 전제 조건은 유형 삭제를 이해하는 것입니다.

Java의 제네릭은 기본적으로 컴파일러 수준에서 구현됩니다. 제네릭의 유형 정보는 생성된 Java 바이트코드에 포함되지 않습니다. 제네릭을 사용할 때 추가된 유형 매개변수는 컴파일 중에 컴파일러에 의해 제거됩니다. 이 프로세스를 유형 삭제라고 합니다.

코드에 정의된 List 및 List과 같은 유형은 컴파일 후에 List에 프로그래밍됩니다. JVM이 보는 모든 것은 목록이며, 제네릭에 의해 첨부된 유형 정보는 JVM에 표시되지 않습니다. Java 컴파일러는 컴파일 타임에 가능한 오류를 찾기 위해 최선을 다하지만 여전히 런타임 시 유형 변환 예외를 피할 수는 없습니다. 유형 삭제는 Java의 일반 구현 방법과 C++ 템플릿 메커니즘 구현 방법(나중에 설명) 사이의 중요한 차이점이기도 합니다.

3. 유형 삭제 후에도 유지되는 원본 유형

원본 유형(원시 유형)은 일반 정보가 삭제된 후 바이트코드에 있는 유형 변수의 실제 유형입니다. 제네릭 유형이 정의될 때마다 해당 기본 유형이 자동으로 제공됩니다. 유형 변수는 지워지고(crased) 정규화된 유형으로 대체됩니다(한정되지 않은 변수는 Object임).

class Pair<T> {    
  private T value;    
  public T getValue() {    
    return value;    
  }    
  public void setValue(T  value) {    
    this.value = value;    
  }    
}

pair의 원래 유형은 다음과 같습니다.

class Pair {    
  private Object value;    
  public Object getValue() {    
    return value;    
  }    
  public void setValue(Object  value) {    
    this.value = value;    
  }    
}

pair에서 T는 정의되지 않은 유형 변수이므로 Object로 대체됩니다. 결과는 Java가 언어에 추가되기 전에 제네릭이 구현된 것처럼 일반 클래스입니다. 프로그램은 pairf7e83be87db5cd2d9a8a0b8117b38cd4 또는 pairc0f559cc8d56b43654fcbe4aa9df7b4a와 같은 다양한 유형의 pair를 포함할 수 있습니다. 그러나 유형을 삭제한 후에는 원래의 pair 유형이 되고 원래 유형은 모두 Object입니다.

유형 변수가 경계형인 경우 원래 유형은 첫 번째 경계 유형 변수로 대체됩니다.

예를 들어, pair가 다음과 같이 선언된 경우:

public class Pair<T extends Comparable& Serializable> {

원래 유형은 Comparable입니다

참고:

pair가 이 클래스와 같이 공개로 선언된 경우, 원래 유형은 직렬화 가능으로 대체되고 컴파일러는 필요할 때 Comparable에 캐스트를 삽입합니다. 효율성을 위해 태그가 지정된 인터페이스(즉, 메서드가 없는 인터페이스)는 경계 한정 목록의 끝에 배치되어야 합니다.

기본 유형과 일반 변수 유형을 구별하려면

제네릭 메소드를 호출할 때 제네릭 유형을 지정할지 여부를 지정할 수 있습니다.

제네릭을 지정하지 않으면 제네릭 변수의 유형은 Object까지 메소드의 여러 유형 중 동일한 상위 클래스의 가장 낮은 수준입니다.

제네릭을 지정할 때 메서드의 여러 유형은 제네릭 인스턴스 유형이거나 해당 하위 클래스여야 합니다.

public class Test{    
  public static void main(String[] args) {    
    /**不指定泛型的时候*/    
    int i=Test.add(1, 2); //这两个参数都是Integer,所以T为Integer类型    
    Number f=Test.add(1, 1.2);//这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number    
    Object o=Test.add(1, "asd");//这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object    
    
    /**指定泛型的时候*/    
    int a=Test.<Integer>add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类    
    int b=Test.<Integer>add(1, 2.2);//编译错误,指定了Integer,不能为Float    
    Number c=Test.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float    
  }    
  
  //这是一个简单的泛型方法    
  public static <T> T add(T x,T y){    
    return y;    
  }    
}

사실 제네릭 클래스에서는 제네릭 타입을 지정하지 않은 경우에는 이때 제네릭 타입이 Object라는 점만 빼면 거의 동일합니다. 예를 들어 ArrayList의 경우 일반 유형이 지정되지 않은 경우 모든 유형의 객체를 ArrayList에 배치할 수 있습니다.

4. C++ 템플릿 구현

C++는 모르지만 인터넷에서 C++ 구현을 찾아보기도 했습니다.

C++에서 템플릿의 각 인스턴스화에 대해 서로 다른 유형을 생성하는 것은 "템플릿 코드 팽창"이라고 알려진 현상입니다.

예를 들어 벡터, 벡터, 벡터 등 총 3가지 벡터 코드가 생성됩니다.



Java 제네릭의 구현 원리와 관련된 더 많은 기사를 보려면 PHP 중국어 웹사이트를 주목하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.