[java] Introducing

제네릭 소개

제네릭 의약품을 사용하는 이유

간단히 말해서 제네릭은 클래스, 인터페이스 및 메서드를 정의할 때 형식(클래스 및 인터페이스)이 매개 변수가 되도록 허용합니다.
메서드 선언에 사용되는 보다 친숙한 형식 매개 변수와 마찬가지로 형식 매개 변수는 다른 입력과 함께 동일한 코드를 재사용하는 방법을 제공합니다.
형식 매개변수의 입력은 값인 반면 유형 매개변수의 입력은 유형이라는 차이점이 있습니다.

제네릭을 사용하는 코드는 제네릭이 아닌 코드에 비해 많은 이점이 있습니다.

  • 더 강력한 컴파일 타임 유형 검사. Java 컴파일러는 간단한 코드에 대해 강력한 유형 검사를 시행하고 코드가 유형 안전을 위반하는 경우 오류를 발생시킵니다.
    컴파일 타임 오류를 수정하는 것은 찾기 어려울 수 있는 런타임 오류를 수정하는 것보다 쉽습니다.
  • 석고 제거. 다음의 제네릭이 아닌 코드 스니펫에는 캐스팅이 필요합니다.
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

제네릭을 사용하도록 코드를 다시 작성하면 캐스팅할 필요가 없습니다.

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast
  • 프로그래머가 일반 알고리즘을 구현할 수 있습니다.
    제네릭을 사용하면 프로그래머는 다양한 유형의 컬렉션에서 작동하고 사용자 지정이 가능하고 유형이 안전하며 읽기 쉬운 제네릭 알고리즘을 구현할 수 있습니다.

제네릭 유형

간단한 상자 클래스

제네릭 형식은 형식에 대해 매개 변수화된 제네릭 클래스 또는 인터페이스입니다.
다음 Box 클래스는 개념을 설명하기 위해 수정되었습니다.

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

이 메서드는 수락하거나 반환하기 때문에 물체기본 유형 중 하나가 아닌 경우 원하는 것을 전달할 수 있습니다.
컴파일 타임에 클래스가 사용되는 방식을 결정할 방법이 없습니다.
코드의 일부 정수상자에 a를 넣고 상자에서 유형의 개체를 가져올 것으로 예상합니다.
정수반면에 코드의 다른 부분은 런타임 오류가 발생할 수 있습니다.

Box 클래스의 제네릭 버전

제네릭 클래스는 다음 형식으로 정의됩니다.

class name<T1, T2, ..., Tn> { /* ... */ }

꺾쇠 괄호( )로 구분된 유형 매개변수 섹션은 클래스 이름 <> 다음에 옵니다.
유형 매개변수(유형 변수라고도 함) T1, T2, … 및 Tn을 지정합니다.

제네릭을 사용하도록 클래스를 업데이트하려면 box 코드를 public class Box에서 public class Box로 변경합니다.
‘ 제네릭 형식 선언을 만듭니다.
이렇게 하면 클래스 내 어디에서나 사용할 수 있는 T 유형의 변수가 도입됩니다.

이 변경으로 Box 클래스는 다음과 같이 됩니다.

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

에서 모든 항목을 볼 수 있듯이 물체T로 대체됩니다.
유형 변수는 지정하는 기본 유형이 아닌 모든 유형이 될 수 있습니다.
클래스 유형, 인터페이스 유형, 배열 유형 또는 기타 유형 변수.

동일한 기술을 적용하여 일반 인터페이스를 만들 수 있습니다.

매개변수 이름 지정 규칙 입력

규칙에 따라 유형 매개변수 이름은 단일 대문자입니다.
이것은 여러분이 이미 알고 있는 변수 명명 규칙과 극명한 대조를 이루며 그럴만한 이유가 있습니다.
이 규칙이 없으면 형식 변수와 일반 클래스 또는 인터페이스 이름 간의 차이점을 구분하기 어려울 것입니다.

가장 일반적으로 사용되는 유형 매개변수 이름은 다음과 같습니다.

  • E – 요소(Java Collections Framework에서 광범위하게 사용됨)
  • K-키
  • N – 숫자
  • T형
  • V-값
  • S, U, V 등 – 2차, 3차, 4차
  • 이러한 이름은 Java SE API 및 이 섹션의 나머지 부분에서 사용됩니다.

일반 유형 호출 및 인스턴스화

박스 코드 내에서 일반 클래스를 참조하려면 T를 다음과 같은 구체적인 값으로 대체하여 일반 유형 호출을 수행해야 합니다.
정수.

Box<Integer> integerBox;

제네릭 형식 호출을 일반 메서드 호출과 유사하게 생각할 수 있지만 메서드에 인수를 전달하는 대신 인수( 정수이 경우) Box 클래스 자체에.

유형 매개변수 및 유형 인수 용어: 많은 개발자가 “유형 매개변수”와 “유형 인수”라는 용어를 같은 의미로 사용하지만 이 용어는 동일하지 않습니다.
코딩할 때 유형 인수를 제공하여 매개변수화된 유형을 생성합니다.
틴 푸 따라서 유형 매개변수이며 푸에서 f는 형식 인수입니다.
이 섹션에서 이러한 용어를 사용할 때 당사는 이러한 정의를 준수합니다.

다른 변수 선언과 마찬가지로 이 코드는 실제로 새 Box 객체를 생성하지 않습니다.
그것은 단순히 integerBox가 “정수 상자”에 대한 참조를 포함한다고 선언합니다.
상자는 이렇게 됩니다 읽었다.

제네릭 형식에 대한 호출은 일반적으로 매개 변수화된 형식이라고 합니다.

이 클래스를 인스턴스화하려면 평소와 같이 new 키워드를 사용하되 대괄호와 클래스 이름 사이.

Box<Integer> integerBox = new Box<Integer>();

다이아몬드

Java SE 7 이상에서는 컴파일러가 컨텍스트에서 유형 인수를 결정하거나 유추할 수 있는 한 일반 클래스의 생성자를 호출하는 데 필요한 유형 인수를 빈 유형 인수 세트( <> )로 대체할 수 있습니다.
이 꺽쇠 괄호 <> 쌍은 비공식적으로 해시라고 합니다.
Box의 인스턴스를 만들 수 있습니다.
예를 들어 다음 명령문을 사용하여 생성합니다.

Box<Integer> integerBox = new Box<>();

해시 표기법 및 유형 유추에 대한 자세한 내용은 이 자습서의 유형 유추 섹션을 참조하십시오.

다중 유형 매개변수

앞서 언급했듯이 제네릭 클래스는 여러 유형 매개변수를 가질 수 있습니다.
예를 들어 일반 인터페이스 OrderedPair를 구현하는 일반 클래스는 다음과 같습니다.

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
    this.key = key;
    this.value = value;
    }

    public K getKey()    { return key; }
    public V getValue() { return value; }
}

다음 문은 OrderedPair 클래스의 두 인스턴스화를 만듭니다.

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");

코드는 새로운 OrderedPair로 시작합니다.
() 그리고 . 따라서 생성자의 매개변수 유형은 of 및 입니다.
autoboxing으로 인해 클래스에 a 및 on을 전달하는 것이 유효합니다.
KV정수질서 있는 쌍정수내부

해시 섹션에서 언급한 것처럼 Java 컴파일러는 선언에서 K 및 유형을 유추할 수 있으므로 해시 표기법을 사용하여 다음 명령문을 축약할 수 있습니다.

OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String>  p2 = new OrderedPair<>("hello", "world");

제네릭 인터페이스를 만들려면 제네릭 클래스를 만들 때와 동일한 규칙을 따릅니다.

매개변수화된 유형

유형 매개변수(예: K 또는 V)를 매개변수화된 유형(예: )으로 바꿀 수도 있습니다.
목록. 예를 들어 다음 OrderedPair를 사용합니다.
-예:

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

원시 유형

기본 유형은 유형 인수가 없는 일반 클래스 또는 인터페이스의 이름입니다.
다음과 같은 일반 Box 클래스가 있다고 가정합니다.

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

매개변수화된 유형의 상자로 정식 형식 매개 변수 T에 대한 실제 형식 인수를 제공합니다.

Box<Integer> intBox = new Box<>();

실제 유형 인수가 생략되면 결과 기본 유형은 Box입니다.
.

Box rawBox = new Box();

따라서 상자는 일반 상자의 기본 유형입니다.
. 그러나 제네릭이 아닌 클래스 또는 인터페이스 유형은 기본 유형이 아닙니다.

많은 API 클래스(예: Collections 클래스)가 JDK 5.0 이전에는 일반적이지 않았기 때문에 기본 유형은 레거시 코드에 표시됩니다.
원시 유형을 사용할 때 기본적으로 제네릭 이전 동작을 얻습니다.
상자는 개체를 제공합니다.
이전 버전과의 호환성을 위해 매개변수화된 유형을 기본 유형에 할당할 수 있습니다.

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;               // OK

그러나 원시 유형을 매개변수화된 유형에 할당하면 다음 경고가 표시됩니다.

Box rawBox = new Box();           // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;     // warning: unchecked conversion

기본 유형을 사용하여 해당 제네릭 유형에 정의된 제네릭 메서드를 호출하는 경우에도 경고가 표시됩니다.

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

이 경고는 원시 유형이 일반 유형 검사를 우회하여 안전하지 않은 코드 감지를 런타임으로 연기함을 보여줍니다.
따라서 원시 유형을 사용하지 않아야 합니다.

유형 삭제 섹션은 Java 컴파일러가 기본 유형을 사용하는 방법에 대한 자세한 정보를 제공합니다.

확인되지 않은 오류 메시지

앞서 언급했듯이 레거시 코드와 일반 코드를 혼합하면 다음과 유사한 경고 메시지가 나타날 수 있습니다.

Note: Example.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

이는 아래 예와 같이 원시 유형으로 작동하는 레거시 API를 사용할 때 발생할 수 있습니다.

public class WarningDemo {
    public static void main(String() args){
        Box<Integer> bi;
        bi = createBox();
    }

    static Box createBox(){
        return new Box();
    }
}

“확인되지 않은”이라는 용어는 컴파일러에 형식 안전성을 보장하는 데 필요한 모든 형식 검사를 수행할 수 있는 충분한 형식 정보가 없음을 의미합니다.
확인되지 않은 경고는 기본적으로 비활성화되어 있지만 컴파일러에서 힌트를 제공합니다.
-Xlint:모든 “선택되지 않은” 경고를 표시하려면 선택하지 않습니다.

-Xlint:unchecked를 사용하여 이전 예제를 다시 컴파일하면 추가 정보가 표시됩니다.

WarningDemo.java:4: warning: (unchecked) unchecked conversion
found   : Box
required: Box<java.lang.Integer>
        bi = createBox();
                      ^
1 warning

확인되지 않은 경고를 완전히 비활성화하려면 -Xlint:-unchecked 플래그를 사용하십시오. 주석은 @SuppressWarnings(“확인되지 않음”)확인되지 않은 경고를 억제합니다.
구문에 익숙하지 않은 경우 @SuppressWarnings의견 섹션을 참조하십시오.

제네릭 메서드

제네릭 메서드는 자체 형식 매개 변수를 도입하는 메서드입니다.
제네릭 형식을 선언하는 것과 비슷하지만 형식 매개 변수의 범위가 선언된 메서드로 제한됩니다.
정적 및 비정적 제네릭 메서드와 제네릭 클래스 생성자가 허용됩니다.

제네릭 메서드의 구문에는 메서드의 반환 형식 앞에 꺾쇠 괄호로 묶인 형식 매개 변수 목록이 포함됩니다.
정적 제네릭 메서드의 경우 형식 매개 변수 섹션은 메서드의 반환 형식 앞에 나타나야 합니다.

이 util 클래스에는 두 쌍 개체를 비교하는 일반 비교 메서드가 포함되어 있습니다.

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

public class Pair<K, V> {

    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

이 메서드를 호출하는 전체 구문은 다음과 같습니다.

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

유형은 굵게 표시된 대로 명시적으로 제공되었습니다.
일반적으로 이것은 생략할 수 있으며 컴파일러는 필요한 유형을 유추합니다.

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

유형 유추라고 하는 이 기능을 사용하면 꺾쇠 괄호 사이에 유형을 지정하지 않고 제네릭 메서드를 일반 메서드로 호출할 수 있습니다.
이 항목은 다음 섹션 유형 유추에서 자세히 다룹니다.

제한된 유형 매개변수

매개변수화된 유형에서 유형 인수로 사용할 수 있는 유형을 제한하려는 경우가 있을 수 있습니다.
예를 들어 숫자로 작업하는 메서드는 인스턴스입니다.
숫자또는 이러한 하위 클래스만 허용할 수도 있습니다.
이것이 제한된 유형 매개변수의 목적입니다.

구분된 유형 매개변수를 선언하려면 유형 매개변수의 이름, extend 키워드, 상한값을 나열하십시오. 이 예에서는 숫자. 이 문맥에서 “extends”(클래스에서와 같이) 또는 “extends”(인터페이스에서와 같이)는 .extendsimplements의 일반적인 의미로 사용됩니다.

public class Box<T> {

    private T t;          

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }

    public <U extends Number> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }

    public static void main(String() args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.set(new Integer(10));
        integerBox.inspect("some text"); // error: this is still String!
} }

이 제한된 유형 매개변수를 포함하도록 제네릭 메서드를 변경하면 검사 호출에 여전히 다음이 포함되어 있으므로 컴파일이 실패합니다.
.

Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot
  be applied to (java.lang.String)
                        integerBox.inspect("10");
                                  ^
1 error

제네릭 형식을 인스턴스화하는 데 사용할 수 있는 형식을 제한하는 것 외에도 제한된 형식 매개 변수를 사용하면 범위에 정의된 메서드를 호출할 수 있습니다.

public class NaturalNumber<T extends Integer> {

    private T n;

    public NaturalNumber(T n)  { this.n = n; }

    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }

    // ...
}

이 메서드는 를 통해 Integer 클래스에 정의된 isEven() 메서드를 호출합니다.
정수값()N

다중 경계

앞의 예제는 단일 경계 유형 매개변수의 사용을 보여주지만 유형 매개변수는 여러 경계를 가질 수 있습니다.

<T extends B1 & B2 & B3>

다중 경계 유형 변수는 범위에 나열된 모든 유형의 하위 유형입니다.
범위 중 하나가 클래스인 경우 먼저 지정해야 합니다.
예를 들어:

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }

bound가 먼저 지정되지 않으면 컴파일 타임 오류가 발생합니다.

class D <T extends B & A & C> { /* ... */ }  // compile-time error

제네릭 메서드 및 제한된 형식 매개변수

구분된 유형 매개변수는 제네릭 알고리즘을 구현하는 데 핵심입니다.
지정된 요소보다 큰 배열의 요소 수를 계산하려면 다음 T() 메서드를 고려하십시오.

public static <T> int countGreaterThan(T() anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // compiler error
            ++count;
    return count;
}

메소드의 구현은 쉽지만 보다 큼 연산자( )가 , , , 및 >와 같은 기본 유형에만 적용되기 때문에 컴파일되지 않습니다.
연산자를 사용하여 개체를 비교할 수 없습니다.
문제를 해결하려면 .shortintdoublelongfloatbytechar> 인터페이스에 래핑된 유형 매개변수를 사용하십시오.유사한

public interface Comparable<T> {
    public int compareTo(T o);
}

결과 코드는 다음과 같습니다.

public static <T extends Comparable<T>> int countGreaterThan(T() anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

제네릭, 상속 및 하위 유형 지정

이미 알고 있듯이 유형이 호환되는 경우 한 유형의 개체를 다른 유형의 개체에 할당할 수 있습니다.
예를 들어 an은 an의 상위 유형 중 하나이므로 정수배정될 수 있다물체물체정수

Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger;   // OK

객체지향 용어로 이것을 “is-a” 관계라고 합니다.
이다 정수개체 유형이므로 할당이 허용됩니다.
하지만 정수또한 종류 숫자다음 코드도 유효합니다.

public void someMethod(Number n) { /* ... */ }

someMethod(new Integer(10));   // OK
someMethod(new Double(10.1));   // OK

제네릭에도 동일하게 적용됩니다.
유형 인수로 전달하여 일반 유형 호출을 수행할 수 있습니다.
숫자인수가 호환되는 경우 Add에 대한 모든 후속 호출이 허용됩니다.
숫자.

Box<Number> box = new Box<Number>();
box.add(new Integer(10));   // OK
box.add(new Double(10.1));  // OK

이제 다음 방법을 고려하십시오.

public void boxTest(Box<Number> n) { /* ... */ }

어떤 유형의 청구를 수락합니까? 서명을 보면 Box 유형의 단일 인수를 취하는 것을 볼 수 있습니다.
받아 들였습니다.
그러나 그것은 무엇을 의미합니까? 당신은 예상대로 상자 수 아니면 하나 넘겨? 상자내 대답은 아니오 야”. 박스 때문입니다 그리고 상자 상자의 하위 유형 없음 이다.

이것은 제네릭 프로그래밍에 대한 일반적인 오해이지만 배워야 할 중요한 개념입니다.
Box의 하위 유형입니다.
그러나 Box의 하위 유형은 아님정수숫자


제네릭 소개

매개변수화된 유형의 하위 유형.

힌트: 예를 들어 A와 B라는 두 가지 유형의 콘크리트가 주어집니다.
숫자및 여부와 관련이 없습니다.
x와 x의 공통 조상은 x입니다.
정수내 수업내 수업AB나의 수업내 수업물체

유형 매개변수가 관련될 때 두 제네릭 클래스 간에 하위 유형과 같은 관계를 만드는 방법에 대한 자세한 내용은 다음을 참조하세요. 와일드카드 및 하위 유형 지정 섹션을 참조하십시오.

일반 클래스 및 하위 유형 지정

일반 클래스 또는 인터페이스를 확장하거나 구현하여 하위 유형을 지정할 수 있습니다.
한 클래스 또는 인터페이스의 형식 매개 변수와 다른 클래스 또는 인터페이스의 형식 매개 변수 간의 관계는 extend 및 implements 절에 의해 결정됩니다.

Collections 클래스를 예로 사용 배열목록구현 목록하다 목록확장하다 수집. 의 하위 유형인 하위 유형에 대해서도 마찬가지입니다.
배열목록볼 수 없습니다.
유형 인수를 변경하지 않는 한 하위 유형 지정 관계는 유형 간에 유지됩니다.
목록수집


제네릭 의약품 소개

샘플 수집 계층.

PayloadList 이제 각 요소에 일반 유형 P의 선택적 값을 할당하는 고유한 목록 인터페이스를 정의한다고 상상해 보십시오. 선언은 다음과 같을 수 있습니다.

interface PayloadList<E,P> extends List<E> {
  void setPayload(int index, P val);
  ...
}

다음 매개변수화는 PayloadList의 하위 유형입니다.
목록.

  • 페이로드 목록
  • 페이로드 목록
  • 페이로드 목록

제네릭 소개

페이로드 계층 구조의 예.