Java java지도 시간 자바 제네릭

자바 제네릭

Dec 19, 2016 pm 02:54 PM
제네릭

1. 제네릭 개념의 도입(제네릭이 필요한 이유)

먼저 다음 단축 코드를 살펴보겠습니다.

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 유형의 컬렉션을 정의하고 여기에 두 개의 문자열 유형 값을 먼저 추가한 다음 A 값을 추가합니다. 정수 유형입니다. 목록의 기본 유형이 개체이기 때문에 이는 완전히 허용됩니다. 후속 루프에서는 이전에 목록에 Integer 유형 값을 추가하는 것을 잊어버렸거나 다른 인코딩 이유로 인해 //1과 유사한 오류가 쉽게 발생할 수 있습니다. 컴파일 단계는 정상인데 런타임 중에 "java.lang.ClassCastException" 예외가 발생하기 때문입니다. 따라서 코딩 중에 이러한 오류를 감지하기가 어렵습니다.

위의 코딩 과정에서 두 가지 주요 문제가 있음을 발견했습니다.

1. 객체를 컬렉션에 넣을 때 컬렉션이 객체의 유형을 기억하지 못합니다. 이 객체를 컬렉션에서 다시 가져오면 객체의 컴파일된 유형이 Object 유형으로 변경되지만 런타임 유형은 여전히 ​​자체 유형입니다.

2. 따라서 //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을 통해 목록 컬렉션에 String만 포함될 수 있도록 직접적으로 제한됩니다. type.요소이므로 //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 인터페이스의 구현 클래스이고 정의 형식은 다음과 같습니다.

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);
    }
    
    //...省略掉其他具体的定义过程

}
로그인 후 복사

이로 인해 Integer 유형 객체가 추가된 이유를 소스 코드 관점에서 이해할 수 있습니다. at //1 컴파일 오류가 발생했으며 //2에서 get()으로 얻은 유형은 바로 String 유형입니다.

3. 사용자 정의된 제네릭 인터페이스, 제네릭 클래스 및 제네릭 메서드

위 내용을 통해 모두가 제네릭의 구체적인 작업 프로세스를 이해했습니다. 또한 인터페이스, 클래스 및 메서드도 제네릭을 사용하여 정의하고 그에 따라 사용할 수 있다는 것을 알고 있습니다. 예, 특정 용도에서는 일반 인터페이스, 일반 클래스 및 일반 메서드로 나눌 수 있습니다.

사용자 정의된 일반 인터페이스, 일반 클래스 및 일반 메소드는 위 Java 소스 코드의 List 및 ArrayList와 유사합니다. 다음은 제네릭 클래스와 메소드에 대한 가장 간단한 정의를 살펴보겠습니다.

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 매개변수 형식을 보게 됩니다. of, 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)입니다. 물론 논리적으로는 여러 다른 일반 유형으로 이해할 수 있습니다.

이유는 Java에서 제네릭 개념의 목적은 코드 컴파일 단계에서만 작동한다는 것입니다. 컴파일 과정에서 제네릭 결과가 올바르게 검증되면 제네릭이 관련 정보가 됩니다. 즉, 성공적으로 컴파일된 클래스 파일에는 일반 정보가 포함되어 있지 않습니다. 일반 정보는 런타임 단계에 들어 가지 않습니다.

이것은 한 문장으로 요약할 수 있습니다. 일반 유형은 논리적으로 여러 다른 유형으로 볼 수 있지만 실제로는 모두 동일한 기본 유형입니다.

4. 와일드카드 입력

接着上面的结论,我们知道,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 ype GenericTest 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? 还是Number?且由于在编程过程中的顺序不可控性,导致在必要的时候必须要进行类型判断,且进行强制类型转换。显然,这与泛型的理念矛盾,因此,在逻辑上Box不能视为Box的父类。

好,那我们回过头来继续看“类型通配符”中的第一个例子,我们知道其具体的错误提示的深层次原因了。那么如何解决呢?总部能再定义一个新的函数吧。这和Java中的多态理念显然是违背的,因此,我们需要一个在逻辑上可以用来表示同时是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形式,其含义与类型通配符上限正好相反,在此不作过多阐述了。

 

五.话外篇

本文中的例子主要是为了阐述泛型中的一些思想而简单举出的,并不一定有着实际的可用性。另外,一提到泛型,相信大家用到最多的就是在集合中,其实,在实际的编程过程中,自己可以使用泛型去简化开发,且能很好的保证代码质量。并且还要注意的一点是,Java中没有所谓的泛型数组一说。

对于泛型,最主要的还是需要理解其背后的思想和目的。


更多Java泛型相关文章请关注PHP中文网!

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

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

일반 함수가 Golang의 가변 매개변수 유형 문제를 해결합니까? 일반 함수가 Golang의 가변 매개변수 유형 문제를 해결합니까? Apr 16, 2024 pm 06:12 PM

Go의 일반 함수는 가변 유형의 문제를 해결합니다. 일반 함수를 사용하면 런타임에 유형 매개변수를 지정할 수 있습니다. 이를 통해 다양한 유형의 인수를 처리할 수 있는 함수를 작성할 수 있습니다. 예를 들어 Max 함수는 두 개의 비교 가능한 매개변수를 허용하고 더 큰 값을 반환하는 일반 함수입니다. 일반 함수를 사용하면 다양한 유형의 매개변수를 처리할 수 있는 보다 유연하고 일반적인 코드를 작성할 수 있습니다.

golang의 제네릭의 특정 애플리케이션 시나리오 golang의 제네릭의 특정 애플리케이션 시나리오 May 04, 2024 am 11:45 AM

Go의 제네릭 적용 시나리오: 컬렉션 작업: 필터링과 같은 모든 유형에 적합한 컬렉션 작업을 만듭니다. 데이터 구조: 큐, 스택, 맵과 같은 범용 데이터 구조를 작성하여 다양한 유형의 데이터를 저장하고 조작합니다. 알고리즘: 다양한 유형의 데이터를 처리할 수 있는 정렬, 검색, 축소 등의 범용 알고리즘을 작성합니다.

Java 함수 제네릭의 상한과 하한은 무엇입니까? 사용하는 방법? Java 함수 제네릭의 상한과 하한은 무엇입니까? 사용하는 방법? Apr 26, 2024 am 11:45 AM

Java 함수 제네릭을 사용하면 상한 및 하한을 설정할 수 있습니다. 확장은 함수에서 허용하거나 반환하는 데이터 유형이 지정된 유형의 하위 유형이어야 함을 지정합니다. 하한(슈퍼)은 함수에서 허용하거나 반환하는 데이터 유형이 지정된 유형의 슈퍼 유형이어야 함을 지정합니다. 제네릭을 사용하면 코드 재사용성과 보안이 향상됩니다.

Android 개발에 Java 제네릭 적용 Android 개발에 Java 제네릭 적용 Apr 12, 2024 pm 01:54 PM

Android 개발에 제네릭을 적용하면 코드 재사용성, 보안 및 유연성이 향상됩니다. 구문은 유형 매개변수화된 데이터를 조작하는 데 사용할 수 있는 유형 변수 T를 선언하는 것으로 구성됩니다. 작동 중인 일반 항목에는 사용자 정의 데이터 어댑터가 포함되어 있어 어댑터가 모든 유형의 사용자 정의 데이터 개체에 적응할 수 있습니다. Android는 또한 다양한 유형의 매개변수를 조작할 수 있는 일반 목록 클래스(예: ArrayList)와 일반 메서드를 제공합니다. 제네릭 사용의 이점에는 코드 재사용성, 보안 및 유연성이 포함되지만, 코드 가독성을 보장하기 위해 올바른 경계를 지정하고 이를 적당히 사용하도록 주의를 기울여야 합니다.

Golang 제네릭이 함수 서명 및 매개변수에 미치는 영향은 무엇입니까? Golang 제네릭이 함수 서명 및 매개변수에 미치는 영향은 무엇입니까? Apr 17, 2024 am 08:39 AM

Go 함수 서명 및 매개변수에 대한 제네릭의 영향은 다음과 같습니다. 유형 매개변수: 함수 서명에는 함수가 사용할 수 있는 유형을 지정하는 유형 매개변수가 포함될 수 있습니다. 유형 제약 조건: 유형 매개 변수에는 충족해야 하는 조건을 지정하는 제약 조건이 있을 수 있습니다. 매개변수 유형 유추: 컴파일러는 지정되지 않은 유형 매개변수의 유형을 유추할 수 있습니다. 유형 지정: 일반 함수를 호출하기 위해 매개변수 유형을 명시적으로 지정할 수 있습니다. 이를 통해 코드 재사용성과 유연성이 향상되어 여러 유형과 함께 사용할 수 있는 함수 및 유형을 작성할 수 있습니다.

Golang에서 제네릭의 장점과 용도를 살펴보세요. Golang에서 제네릭의 장점과 용도를 살펴보세요. Apr 03, 2024 pm 02:03 PM

답변: Golang 제네릭은 코드 재사용성, 유연성, 유형 안전성 및 확장성을 개선하기 위한 강력한 도구입니다. 자세한 설명: 장점: 코드 재사용성: 공통 알고리즘 및 데이터 구조 유연성: 특정 유형의 인스턴스의 런타임 생성 유형 안전성: 컴파일 시간 유형 확인 확장성: 손쉬운 확장 및 사용자 정의 목적: 공통 기능: 정렬, 비교 목록과 같은 공통 데이터 구조 , 맵, 스택 등 유형 별칭: 유형 선언 단순화 제한된 제네릭: 유형 안전성 보장

Java 열거형 유형은 제네릭과 어떻게 작동합니까? Java 열거형 유형은 제네릭과 어떻게 작동합니까? May 04, 2024 am 08:36 AM

Java에서 열거형 유형과 제네릭의 조합: 제네릭으로 열거형을 선언할 때 꺾쇠 괄호를 추가해야 하며 T는 유형 매개변수입니다. 일반 클래스를 생성할 때 꺾쇠 괄호도 추가해야 합니다. T는 모든 유형을 저장할 수 있는 유형 매개변수입니다. 이 조합은 코드 유연성, 유형 안전성을 향상하고 코드를 단순화합니다.

Golang의 일반 함수에는 어떤 제한이 있나요? Golang의 일반 함수에는 어떤 제한이 있나요? Apr 16, 2024 pm 05:12 PM

Go 일반 함수의 제한 사항: 유형 매개변수만 지원되고 값 매개변수는 지원되지 않습니다. 함수 재귀는 지원되지 않습니다. 유형 매개변수는 명시적으로 지정할 수 없으며 컴파일러에 의해 유추됩니다.

See all articles