추론 알고리즘은 아규먼트의 타입과 (가능한 경우)반환되는 타입을 결정합니다. 마지막으로 추론 알고리즘은 모든 아규먼트와 함께 작동하는 특정 타입을 찾으려고 시도합니다.
이 마지막 요점을 설명하기 위해 다음 예제에서 타입 추론은 pick 메서드에 전달되는 두 번째 아규먼트가 Serializable 유형임을 확인합니다.
static <T> T pick(T a1, T a2) { return a2; } // T = single type parameter
Serializable s = pick("d", new ArrayList<String>());
위 코드를 컴파일러가 (T a1, T a2) 두개를 다 보고 type inference 함. 컴파일러는 T를 seriallizable로 결정.
Serializable s = <String, ArrayList<String>>pick("d", new ArrayList<String>());
Box 클래스가 필요한 다음 예제 BoxDemo를 고려하십시오.
public class BoxDemo {
public static <U> void addBox(U u,
java.util.List<Box<U>> boxes) {
Box<U> box = new Box<>();
box.set(u);
boxes.add(box);
} // U = Integer.valueOf(10)[integer type].
public static <U> void outputBoxes(java.util.List<Box<U>> boxes) {
int counter = 0;
for (Box<U> box: boxes) {
U boxContents = box.get();
System.out.println("Box #" + counter + " contains [" +
boxContents.toString() + "]");
counter++;
}
} // U = integer
public static void main(String[] args) {
java.util.ArrayList<Box<Integer>> listOfIntegerBoxes =
new java.util.ArrayList<>();
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
//addBox generic method.
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
BoxDemo.outputBoxes(listOfIntegerBoxes);
}
}// listOfIntegerBoxes의 type은 Integer.
The following is the output from this example.
Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]
제네릭 메소드 addBox는 U라는 하나의 타입 파라미터를 정의합니다.
일반적으로 Java 컴파일러는 제네릭 메소드 호출의 타입 파라미터를 유추할 수 있습니다. 따라서 대부분의 경우 이를 지정할 필요가 없습니다.
예를 들어 제네릭 메서드 addBox를 호출하려면 다음과 같이 타입 감시를 사용하여 타입 파라미터를 지정할 수 있습니다.
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
또는 타입 감시를 생략하면 Java 컴파일러가 타입 파라미터가 Integer라고 자동으로 추론합니다(메소드의 아규먼트에서).
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
예를 들어 다음 변수 선언을 고려하십시오.
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
생성자의 파라미터화된 타입을 빈 타입 파라미터 세트(<>)로 대체할 수 있습니다.
Map<String, List<String>> myMap = new HashMap<>();
제네릭 클래스 인스턴스화 중에 타입 유추를 활용하려면 다이아몬드를 사용해야 합니다. 다음 예제에서 컴파일러는 HashMap() 생성자가 Map<String, List<String>> 타입이 아닌 HashMap 원시(Raw) 타입을 참조하기 때문에 확인되지 않은 변환 경고를 생성합니다.
Map<String, List<String>> myMap = new HashMap();
// unchecked conversion warning
// "<>"를 안쓰면 raw type.
생성자는 제네릭 클래스와 제네릭이 아닌 클래스 모두에서 제네릭(즉, 자신의 formal 타입 파라미터를 선언)할 수 있습니다.
다음 예를 고려하십시오.
class MyClass<X> {
<T> MyClass(T t) {
// ...
}
} // <X> 는 클래스 내부, <T> constructor 내부.
//<T> type parameter, (T t) fomal parameter.
MyClass 클래스의 다음 인스턴스화를 고려하십시오.
new MyClass<Integer>("") // ("") = constructor. 컨스트럭터가 generic method.
위 코드는 파라미터화된 타입 MyClass<Integer>의 인스턴스를 작성합니다.
이 코드는 제네릭 클래스 MyClass<X>의 formal 타입 파라미터 X에 대해 Integer 타입을 명시적으로 지정합니다.
하지만 이 제네릭 클래스의 생성자에는 formal 타입 파라미터 T가 포함되어 있습니다. 컴파일러는 이 제네릭 클래스 생성자의 formal 타입 파라미터 T에 대해 String 타입을 유추합니다(이 생성자의 실제 타입파라미터는 String 개체이기 때문).
Java SE 7 이전 릴리스의 컴파일러는 제너릭 메소드와 유사하게 제너릭 생성자의 실제 타입 파라미터를 유추할 수 있습니다. 그러나 Java SE 7 이상의 컴파일러는 다이아몬드(<>)를 사용하는 경우 인스턴스화되는 제너릭 클래스의 실제 타입 파라미터를 유추할 수 있습니다.
다음 예를 고려하십시오.
MyClass<Integer> myObject = new MyClass<>("");
이 예제에서 컴파일러는 제네릭 클래스 MyClass<X>의 formal 타입 파라미터 X에 대해 Integer 타입을 유추합니다. 이 제네릭 클래스 생성자의 formal 타입 파라미터 T에 대한 String 타입을 유추합니다.
다음과 같이 선언된 Collections.emptyList 메소드를 고려하십시오.
static <T> List<T> emptyList();
Consider the following assignment statement.
List<String> listOne = Collections.emptyList();
//'<T>' 타입 파라미터를 string으로 바꿈.
위 코드는 List<String>의 인스턴스를 기대하고 있습니다. 이 데이터 타입은 타겟 타입입니다.
emptyList 메서드는 List<T> 타입의 값을 반환하기 때문에 컴파일러는 타입 아규먼트 T가 String 값이어야 한다고 추론합니다.
이는 Java SE 7 및 8 모두에서 작동합니다. 또는 타입 감시를 사용하고 다음과 같이 T 값을 지정할 수 있습니다.
List<String> listOne = Collections.<String>emptyList();
그러나 이 컨텍스트에서는 타입 감시(<String>)가 필요하지 않습니다.
하지만 다른 상황에서는 필요했습니다.
다음 방법을 고려하십시오.
void processStringList(List<String> stringList) {
// process stringList
}
emptyList 메서드 호출로 processStringList 메서드에게 아규먼트를 전달하는 메서드 호출한다고 가정합니다. Java SE 7에서 다음 명령문은 컴파일되지 않습니다.
processStringList(Collections.emptyList());
The Java SE 7 compiler generates an error message similar to the following:
List<Object> cannot be converted to List<String>
컴파일러는 타입 파라미터 T에 대한 값이 필요하므로 Object 값으로 시작합니다.
따라서 Collections.emptyList를 호출하면 processStringList 메서드와 호환되지 않는 List<Object> 타입의 값이 반환됩니다.
따라서 Java SE 7에서는 다음과 같이 타입 아규먼트의 값을 지정해야 합니다.
processStringList(Collections.<String>emptyList());
Java SE 8에서 더 이상 타입 감시가 필요하지 않습니다. 타겟 타입이 무엇인지에 대한 개념이 확장되어 processStringList 메소드에 대한 아규먼트와 같은 메소드 아규먼트를 포함합니다.
이 경우 processStringList에는List<String>타입의 아규먼트가 필요합니다.
Collections.emptyList 메서드는List<T>의 값을 반환하므로List<String>의 타겟 타입을 사용하여 컴파일러는 타입 아규먼트 T의 값이 String인 것으로 유추합니다. 따라서 Java SE 8에서는 다음 코드가 컴파일됩니다.
processStringList(Collections.emptyList());