Java8의 새로운 특성 범용 유형 유도

1. 범형은 도대체 무엇입니까?
유형 유도 (type inference) 를 토론하기 전에 범용 (Generic) 이 무엇인지 되돌아봐야 한다.범주형은 JavaSE 1.5의 새로운 특성으로 범주형의 본질은 매개 변수화 유형이다. 즉, 조작된 데이터 형식이 하나의 매개 변수로 지정된다는 것이다.일반적인 점은 유형 변수입니다.이런 유형의 변수는 클래스, 인터페이스, 방법의 생성에 사용할 수 있다.Java 범주를 이해하는 가장 간단한 방법은 이를 편리한 문법으로 간주하여 일부 Java 유형 변환 casting 의 조작을 절약하는 것이다.

List<Apple> box = new ArrayList<Apple>();box.add(new Apple());
Apple apple =box.get(0);
위의 코드 자체는 이미 명확하게 표현되었다. 박스는 Apple 대상이 설치된 것이다List.get 방법은 Apple 대상의 실례를 되돌려줍니다. 이 과정은 유형 변환이 필요하지 않습니다.범용 없음, 위의 코드는 다음과 같이 써야 합니다.

Apple apple = (Apple)box.get(0);
물론 범형은 내가 여기서 묘사한 것처럼 결코 이렇게 간단하지 않다. 그러나 이것은 우리의 오늘의 주인공이 아니다. 범형에 대해 아직 잘 모르는 학우들은 보충 수업을 해야 한다. 물론 가장 좋은 참고 자료는 역시 공식 문서이다.
2. 범주에 따른 문제(Java 7 이전)
범형의 가장 큰 장점은 프로그램의 유형을 안전하게 제공하고 뒤로 호환할 수 있다는 것이다. 그러나 개발자를 기분 나쁘게 하는 부분도 있다. 즉, 매번 정의할 때마다 범형의 유형을 명확하게 써야 한다. 이렇게 지정이 좀 지루할 뿐만 아니라 가장 중요한 것은 많은 프로그래머들이 범형에 익숙하지 않기 때문에 정확한 유형 파라미터를 제시하지 못할 때가 많다. 지금은 컴파일러를 통해 범형의 파라미터 유형을 자동으로 추정한다.이런 상황을 줄이고 코드의 가독성을 높일 수 있다.
3. Java 7의 범용 유형 유도에 대한 개선
Java 7 이전 릴리즈에서 범용 유형을 사용하고 값을 명시하고 지정해야 할 때 양쪽에 범용 유형을 추가합니다.예를 들면 다음과 같습니다.

Map<String,Integer> map = new HashMap<String,Integer>(); 
많은 사람들이 처음에는 틀림없이 나와 같았을 것이다. 이것에 대해 매우 이해하지 못한다. 나는 변수 성명에서 이미 매개 변수 유형을 성명하지 않았느냐?왜 대상이 초기화될 때 표시해야 합니까?범형이 처음 등장했을 때 많은 사람들의 비난을 받았던 곳이기도 하다.그러나 기쁘게도 자바는 발전하는 동시에 디자이너들도 자바의 컴파일러를 끊임없이 개선하여 더욱 스마트하고 인성화시키고 있다.여기, 바로 우리 오늘의 주인공: 장르 밀어내기...어...밀어내기가 아니라 유형 유도, 즉 type inference입니다. 이 형제들의 출현, 그리고 이런 코드를 쓸 때 대상이 실례화될 때의 매개 변수 유형을 즐겁게 생략할 수 있습니다.

Map<String,Integer> map = new HashMap<>();
이 문장에서 컴파일러는 변수가 설명할 때의 범용 유형에 따라 실례화HashMap할 때의 범용 유형을 자동으로 추정합니다.다시 한 번 주의하십시오. new HashMap 뒤에 있는 "<>"은 이 "<>"를 붙여야만 자동 형식 추정을 나타냅니다. 그렇지 않으면 비범형 형식 HashMap 이며, 컴파일러를 사용하여 원본 코드를 컴파일할 때 경고 알림 (unchecked conversion warning) 을 줍니다.이 쌍의 괄호 "<>"공식 문서에서는 "diamond"라고 합니다.
그러나 이때의 유형 추정은 완전하지 않다(심지어 반제품이라고 할 수 있다). 왜냐하면 자바 SE 7에서 범용 실례를 만들 때의 유형 추정은 제한이 있기 때문이다. 구조기의 매개 변수화 유형이 상하문에서 현저하게 설명되어야만 유형 추정을 사용할 수 있고 그렇지 않으면 안 된다.예를 들어 다음 예는 자바 7에서 정확하게 컴파일할 수 없습니다. (그러나 지금은 자바 8에서 컴파일할 수 있습니다. 방법의 매개 변수에 따라 범용 유형을 자동으로 추정하기 때문입니다.)

List<String> list = new ArrayList<>();
list.add("A");//  addAll Collection<? extends String> , 
list.addAll(new ArrayList<>());
4. 자바8에서의 재진화
최신 자바 공식 문서에서 우리는 유형 유도에 대한 정의를 볼 수 있다.
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.
간단히 말하면 유형 유도는 컴파일러가 당신이 호출하는 방법과 상응하는 성명에 따라 필요한 매개 변수 유형을 확정할 수 있는 능력을 가리킨다.그리고 공식 문서에서 예를 들어 설명했다.

static <T> T pick(T a1, T a2) { return a2; }
Serializable s = pick("d", new ArrayList<String>());
여기서 컴파일러는 전입pick 방법 중의 두 번째 파라미터의 유형Serializable을 유도할 수 있다.
이전의 자바 버전에서 위의 예가 번역을 통과하려면 다음과 같이 써야 한다.

Serializable s = this.<Serializable>pick("d", new ArrayList<String>());
이렇게 쓴 상세한 원인은 Bruce Eckel의 자바 프로그래밍 사상(제4판)의 범용 장에서 볼 수 있다. 물론 이 책은java6를 바탕으로 한 것이고 이 판본은 아직 유형적으로 이 개념을 유도하지 못했다.이곳을 보면 많은 사람들이 최신 버전의 유형 유도의 강력한 점을 분명히 알 수 있다.이미 범형류의 성명과 실례화 과정에 국한된 것이 아니라 범형 파라미터를 가진 방법까지 확대되었다.
4.1 유형 유도 및 범용 방법(Type Inference and Generic Methods)
새로운 버전의 유형 유도와 범용 방법에 대해 문서에서 약간 복잡한 예를 하나 더 주었다. 내가 여기에 붙인 원리와 위의Serializable 예는 모두 같으면 군더더기 없이 설명하지 않겠다. 공고하고 싶은 것은 다시 한 번 볼 수 있다.

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);
 }
}
위의 코드 출력은 다음과 같습니다.

Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]
다시 한 번 말씀드리지만 범용 방법addBox은 새로운 자바 버전에서 방법 호출에 표시할 유형 설명이 필요하지 않다는 데 중점을 두고 있습니다. 이렇게:

BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
컴파일러는 전송 addBox 의 매개 변수로부터 매개 변수 유형 Integer 을 자동으로 추정할 수 있다.
4.2 유형 유도 및 범용 및 비범용 범용 구조기(Type Inference and Generic Constructors of Generic and Non-Generic Classes)
어...이것은 아마도 영어의 더 좋은 단문일 것이다: Type Inference and Generic Constructors of Generic and Non-Generic Classes
사실 범형구조기는 범형류의 특허품이 아니라 비범형류도 자신의 범형구조기를 충분히 가질 수 있다. 이 예를 보자.

class MyClass<X> {
 <T> MyClass(T t) {
 // ...
 }
}
만약 MyClass 클래스에 대해 다음과 같은 실례화를 한다면

new MyClass<Integer>("")
OK, 여기서 우리는 MyClass의 범참 유형 X은Integer을 나타냈고, 구조기에 대해 컴파일러는 전입된 String 대상("")에 따라 형식 매개 변수 T은String을 유도했다. 이것은java7 버전에서 이미 실현되었는데, 자바8에서 어떤 개선이 있었는가?Java8 이후에 범용 구조기를 가진 범용 클래스의 실례화에 대해 우리는 이렇게 쓸 수 있다.

MyClass<Integer> myObject = new MyClass<>("");
그래, 아니면 이 괄호(<>)인가, 강호 사람들은 다이아몬드라고 부른다. 그러면 우리 컴파일러는 형식 매개 변수 X은Integer, T은String을 자동으로 유도할 수 있다.이것은 사실 우리가 처음Map<String,String>의 예와 매우 비슷하지만 단지 여러 개의 구조기의 범형화일 뿐이다.
주의해야 할 것은 유형 유도는 호출된 매개 변수 유형, 목표 유형 (이것은 곧 설명될 것) 과 되돌아오는 유형 (되돌아오는 것이 있다면) 에 따라 유도할 수 있을 뿐 프로그램 뒤의 일부 수요에 따라 유도할 수 없다는 것이다.
4.3 대상 유형(Target Type)
앞에서 언급한 바와 같이 컴파일러는 목표 유형에 따라 유형을 유도할 수 있다.하나의 표현식의 목표 형식은 컴파일러가 표현식이 나타나는 위치에 따라 필요한 정확한 데이터 형식을 가리킨다.예:

static <T> List<T> emptyList();
List<String> listOne = Collections.emptyList();
여기서 List 은 목표 유형입니다. 여기에 필요한 것은 List<String>이고, Collections.emptyList() 돌아오는 것은 List<T>이기 때문에 이 컴파일러는 T가 틀림없다고 추정합니다.이것은 자바 7과 8에서 모두 OK입니다.그러나 자바 7에서는 다음과 같은 상황에서 정상적으로 컴파일할 수 없습니다.

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

processStringList(Collections.emptyList());
이때 자바7은 다음과 같은 오류 힌트를 제공합니다.

//List<Object> cannot be converted to List<String>
원인: String 되돌아오는 것은Collections.emptyList()  , 이곳의 T는 구체적인 유형이 필요하지만 방법 성명에서 필요한 것List<T>을 추정할 수 없기 때문에 컴파일러는 T에게 String의 값을 주었고 뚜렷하다. Object 으로 전환할 수 없기 때문에List<Object> 자바 7 버전에서 이 방법을 사용해야 한다.

processStringList(Collections.<String>emptyList());
그러나java8에서 목표 유형 개념의 도입으로 인해 여기서 컴파일러가 필요로 하는 것은 List<String>.(즉 이곳의 Target Type)이기 때문에 컴파일러가 되돌아오는 List<String> 중의 T는 반드시 List<T>일 것으로 추정하기 때문에 String 이런 설명은 OK이다.
목표 유형의 사용은 Lambda 표현식에서 가장 뚜렷하다.
총결산
자, 이상은 자바에서의 유형 유도에 대한 개인적인 견해입니다. 총괄적으로 말하자면 점점 완벽해지는 유형 유도는 원래 당연하다고 느끼는 유형 전환 작업을 완성한 것입니다. 단지 이러한 작업은 개발자가 표시하고 지정하는 것이 아니라 컴파일러에게 가득 맡기고 있습니다.이 글의 내용이 여러분의 자바 학습에 도움이 되었으면 좋겠습니다. 궁금한 점이 있으면 댓글을 남겨 주십시오.

좋은 웹페이지 즐겨찾기