용어

statement: 문장으로 해석하면 오해의 여지가 있어서 statement 영문 그대로 작성한다. 컴퓨터 프로그래밍에서는 statement를 독립적인 요소의 최소 단위. 영문위키의 중간을 보면 Kinds of statements 파트에 Simple statments, Compound statements 부분의 코드를 보면 느낌이 온다.


원문링크

타입추론 Type Inference

타입추론은 메서드를 호출하는 코드에서 타입인자가 정의한대로 제대로 쓰였는지 살펴보는 컴파일러의 능력이다. 타입추론은 인자의 타입을 확인하고 만약 사용가능하다면, 해당 타입을 할당하거나 리턴될 것이다. 결국 타입추론은 모든 인자에 동작해서 가장 구체적인 타입을 찾는 것을 시도한다.(뭔가 말이 어렵다;;)
[Type inference is a Java compiler’s ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.] The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.

~아래의 예는 마지막 지점을 설명한다.~ 타입추론은 두번째 인자가 전달될 때 pick 메서드는 Serializable로 결정되어집니다. (역주: pick의 두 인자 a1, a2는 타입인자로 String과 ArrayList를 전달 받는다. 둘의 공통점은 Serializable 인터페이스를 구현한 클래스라는 것이다. 인자들이 같은 타입이 아니면 각각 인자들의 타입에서 공통으로 쓰는 슈퍼 타입으로 추론. 코드레벨로 작성한거라 추론이라하긴 뭐한데… 일단 진행.)
To illustrate this last point, in the following example, inference determines that the second argument being passed to the pick method is of type Serializable:

static <T> T pick(T a1, T a2) { return a2; }
Serializable s = pick("d", new ArrayList<String>());

타입추론과 Generic 메서드 Type Inference and Generic Methods

Generic 메서드는 꺽쇠안에 타입을 작성하지 않고(빈타입인자) 메서드를 호출하면 타입추론을 보여준다. BoxDemo 예를 보시오. 이 예는 Box 클래스를 필요로합니다.
Generic Methods introduced you to type inference, which enables you to invoke a generic method as you would an ordinary method, without specifying a type between angle brackets. Consider the following example, BoxDemo, which requires the Box class:

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);
  }

  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++;
    }
  }

  public static void main(String[] args) {
    java.util.ArrayList<Box<Integer>> listOfIntegerBoxes =
      new java.util.ArrayList<>();
    BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
    BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
    BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
    BoxDemo.outputBoxes(listOfIntegerBoxes);
  }
}

위의 예제는 아래와 같은 결과를 출력한다.
The following is the output from this example:

Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]

generic 메서드인 addbox는 U라는 타입매개변수가 선언되어있다. 일반적으로, 자바컴파일러는 generic 메서드 호출하는 코드를 보고 타입파라메터를 추론할 수 있다. 그 결과, 대부분 타입을 꼭 명시하지 않아도 된다. 예를 들어 addBox라는 generic 메서드를 호출할때, 아래 코드처럼 구체적인 타입 매개변수를 줄 수 있다.
The generic method addBox defines one type parameter named U. Generally, a Java compiler can infer the type parameters of a generic method call. Consequently, in most cases, you do not have to specify them. For example, to invoke the generic method addBox, you can specify the type parameter with a type witness as follows:

BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);

위의 코드 대신 아래 코드처럼 타입 정보를 제공하지 않고 코드를 작성해도, 자바 컴파일러는 타입매개변수가 Integer라고 (메서드의 인자들로부터) 자동으로 추론을 한다.
Alternatively, if you omit the type witness,a Java compiler automatically infers (from the method’s arguments) that the type parameter is Integer:

BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);

타입추론과 generic 객체 Type Inference and Instantiation of Generic Classes

generic 클래스를 생성자를 통해 객체를 생성할 때 타입인자 대신 빈타입파라미터(<>)를 사용할때 자바 컴파일러는 코드 문맥으로부터 타입인자를 유추할 수 있다. 비공식적으로 한쌍의 꺽쇠(<>)는 다이아몬드라고 부른다. ‘You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond.’

예를 들어 변수 선언을 보자.
For example, consider the following variable declaration:

Map<String, List<String>> myMap = new HashMap<String, List<String>>();

(위의 예제 처럼)generic 클래스의 생성자에 타입매개변수화한 생성자 대신 (아래 예제 처럼)<>를 사용할 수 있다.
You can substitute the parameterized type of the constructor with an empty set of type parameters (<>):

Map<String, List<String>> myMap = new HashMap<>();

generic 클래스를 객체화할 때 타입추론을 시키려면 반드시 다이아몬드 연산자를 사용하십시오. 아래의 예를 보면 컴파일러는 Hash맵을 raw타입으로 인식해서 경고를 한다.(선언문의 Map<String, List<String>>을 보고 할당문의 HashMap의 생성자는 타입추론을 하지 않는다. 그냥 raw 타입이 된다. )
Note that to take advantage of type inference during generic class instantiation, you must use the diamond. In the following example, the compiler generates an unchecked conversion warning because the HashMap() constructor refers to the HashMap raw type, not the Map> type:

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

타입추론과 generic 타입의 Generic 생성자 그리고 Non-Generic 클래스


Type Inference and Generic Constructors of Generic and Non-Generic Classes

generic 클래스와 non-generic 클래스는 둘다 generic(일반 타입매개변수를 선언)이 될 수 있다. 아래 예를 보자
Note that constructors can be generic (in other words, declare their own formal type parameters) in both generic and non-generic classes. Consider the following example:

class MyClass<X> {
  <T> MyClass(T t) {
    // ...
  }
}

MyClass 클래스의 객체 생성을 살펴보면
Consider the following instantiation of the class MyClass:

new MyClass<Integer>("")

위의 코드는 MyClass<Integer>라는 매개변수화된 타입의 객체를 생성하는 statement이다. 이 statement는 generic 클래스인 MyClass<X>의 타입매개변수의 형식을 Integer 타입으로 구체화하도록 명시한다. 중요한점은 generic 클래스의 생성자는 T라는 형식 타입매개변수를 포함해야한다. 컴파일러는 generic 클래스의 생성자의 형식 파라메터 T를 String 타입으로 추론한다.(왜냐하면 생성자의 매개변수는 String 타입의 객체이기 때문이다.)
This statement creates an instance of the parameterized type MyClass; the statement explicitly specifies the type Integer for the formal type parameter, X, of the generic class MyClass. Note that the constructor for this generic class contains a formal type parameter, T. The compiler infers the type String for the formal type parameter, T, of the constructor of this generic class (because the actual parameter of this constructor is a String object).

(역자 - 번역하다가 도통 뭔말인지 몰라서 코딩해봄.)

public class Main {
	public static void main(String[] args) {
		MyClass<Integer> a = new MyClass<>("");
		System.out.println(a.x.getClass().getSimpleName());
	}
}

class MyClass<X> {
	X x;
	<T> MyClass(T t) {
		this.x = (X) t;
	}
}
// 런타임 에러 발생되는 코드

(역자)MyClass의 생성자의 T라는 타입매개변수만 적으면 컴파일이 안된다. 생성자 앞에 <T>를 넣어줘야 컴파일이 되는데 요놈을 형식타입매개변수가로 지칭하는 것으로 보인다. 클래스에 적어준 <X>랑은 다름을 주의. 아직은 딱히 어느 상황에 써야할지 잘 모르겠다….

generic 메서드처럼 generic 생성자에서 실제 타입파라메터를 추론하는 능력은 Java SE 7이전 컴파일러에서 릴리즈되었다.(역자??? 이부분이 이해가 안감) 하지만 Java SE 7 이후 컴파일러는 다이아몬드연산자(<>)를 사용해서 generic 클래스를 객체 생성할 때 실제 타입매개변수를 추론할 수 있습니다. 아래의 예를 보세요.
Compilers from releases prior to Java SE 7 are able to infer the actual type parameters of generic constructors, similar to generic methods. However, compilers in Java SE 7 and later can infer the actual type parameters of the generic class being instantiated if you use the diamond (<>). Consider the following example:

MyClass<Integer> myObject = new MyClass<>("");

위의 예제는 컴파일러가 generic 클래스 MyClass<X>의 형식타입 파라메터 X를 Integer 타입으로 추론한다. 컴파일러는 generic 클래스의 생성자의 형식매개변수 T에 String 타입을 추론한다.
In this example, the compiler infers the type Integer for the formal type parameter, X, of the generic class MyClass. It infers the type String for the formal type parameter, T, of the constructor of this generic class.

중요: 타입추론 알고리즘은 인자, 대상 타입, 명백히 기대되는 리턴 타입만 사용한다. 추론 알고리즘은 프로그램에서 나중에 결과를 사용하지 않는다. (역자: 내용이 명확히 안들어옴.)
Note: It is important to note that the inference algorithm uses only invocation arguments, target types, and possibly an obvious expected return type to infer types. The inference algorithm does not use results from later in the program.

Target Types

자바 컴파일러는 generic 메서드 호출에 타입매개변수를 추론하는 target typing은 장점을 가진다. target type 표현식은 자바 컴파일러가 기대하는 데이터 타입이다. Collection.emptyList 메서드를 살펴보면 아래와 같이 선언되어있다.
The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. [The target type of an expression is the data type that the Java compiler expects depending on where the expression appears.] Consider the method Collections.emptyList, which is declared as follows:

static <T> List<T> emptyList();

할당문을 살펴보면:
Consider the following assignment statement:

List<String> listOne = Collections.emptyList();

이 statement는 List의 객체를 기대한다. 이 데이터 타입이 target type이다. 왜냐하면 emptyList 메서드는 List 타입의 값을 리턴한다. 컴파일러는 타입인자는 반드시 String 타입의 값일 것이라고 추론한다. 이는 Java SE 7 이상에서 동작한다. 추론 대신에 아래의 예처럼 타입매개변수 T의 타입인자값을 특정 타입값을 명시할 수 있다.
This statement is expecting an instance of List; this data type is the target type. Because the method emptyList returns a value of type List, the compiler infers that the type argument T must be the value String. This works in both Java SE 7 and 8. Alternatively, you could use a type witness and specify the value of T as follows:

List<String> listOne = Collections.<String>emptyList();

하지만 위의 코드 문맥상 target type이 필요치 않다. 코드문맥상 target type이 필요한 예를 아래 코드를 보며 살펴보자.
However, this is not necessary in this context. It was necessary in other contexts, though. Consider the following method:

void processStringList(List<String> stringList) {
    // process stringList
}

빈리스트와 함께 processStringList 메서드를 호출한다고 가정하자. Java SE 7에서는 아래의 코드는 컴파일 되지 않는다.
Suppose you want to invoke the method processStringList with an empty list. In Java SE 7, the following statement does not compile:

processStringList(Collections.emptyList());

Java SE 7 컴파일러는 아래의 에러메세지를 던진다.
The Java SE 7 compiler generates an error message similar to the following:

List<Object> cannot be converted to List<String>

컴파일러는 T의 타입인자가 필요한데 T는 target type을 주지 않으면 Object이다. 결국, Collection.emptyList에 타입인자는 List 타입이다. 그러므로 Java SE 7은 반드시 아래의 예처럼 타입인자를 명확히 알려줘야한다.
The compiler requires a value for the type argument T so it starts with the value Object. Consequently, the invocation of Collections.emptyList returns a value of type List, which is incompatible with the method processStringList. Thus, in Java SE 7, you must specify the value of the value of the type argument as follows:

processStringList(Collections.<String>emptyList());

Java SE 8에서는 더이상 필요치 않다. 이 표기법은 target type은 메서드 인자를 포함해서 확장했다 (아래 예처럼 processStringList 메서드에 인자를 넣는것처럼). 이런경우, processStringList는 List 타입의 인자를 필요로한다. Collections.emptyList 메서드는 List 타입의 값을 리턴한다. 그럼 List의 target type을 사용하면 컴파일러는 T의 타입인자를 String으로 추론하다. 따라서 Java SE 8은 아래의 문장이 컴파일된다.
This is no longer necessary in Java SE 8. The notion of what is a target type has been expanded to include method arguments, such as the argument to the method processStringList. In this case, processStringList requires an argument of type List. The method Collections.emptyList returns a value of List, so using the target type of List, the compiler infers that the type argument T has a value of String. Thus, in Java SE 8, the following statement compiles:

processStringList(Collections.emptyList());

더 많은 정보는 람다표현식에서 Target Typing을 보세요.
See Target Typing in Lambda Expressions for more information.

comments powered by Disqus