1. 일반적인 것은 무엇입니까?
유형 추론을 논의하기 전에 일반적인 것을 검토해야합니다. 제네릭은 Java SE 1.5의 새로운 기능입니다. 제네릭의 본질은 매개 변수화 된 유형입니다. 즉, 작동하는 데이터 유형은 매개 변수로 지정됩니다. 평신도의 용어로는 "유형 변수"입니다. 이 유형의 변수는 클래스, 인터페이스 및 메소드를 작성하는 데 사용할 수 있습니다. Java 제네릭을 이해하는 가장 쉬운 방법은이를 Java 유형 casting 에서 일부 작업을 절약 할 수있는 편리한 구문으로 간주하는 것입니다.
List <plect> box = new arraylist <plect <plect <plect <plect (); box.add (new Apple ()); Apple Apple = Box.get (0);
위의 코드 자체는 명확하게 표현되었습니다. Box는 Apple Objects가있는 List 입니다. get 메소드는 Apple Object 인스턴스를 반환 하며이 프로세스에는 유형 변환이 필요하지 않습니다. 제네릭이 없으며 위의 코드는 다음과 같이 작성해야합니다.
Apple Apple = (Apple) Box.get (0);
물론, 제네릭은 내가 여기서 설명한 것만 큼 간단하지는 않지만 이것은 우리 시대의 주인공이 아닙니다. 제네릭을 이해하지 못하는 학생들은 수업을 잘 보충해야합니다. 물론 최고의 참조 자료는 여전히 공식 문서입니다.
2. 제네릭으로 인한 문제 (Java 7 이전)
제네릭의 가장 큰 장점은 프로그램의 유형 안전을 제공하고 후진 호환 가능하다는 것입니다. 그러나 개발자를 불행하게 만드는 것들도 있습니다. 제네릭의 유형은 정의 할 때마다 작성해야합니다. 이 디스플레이 사양은 약간의 장점을 느낄뿐만 아니라 가장 중요한 것은 많은 프로그래머가 제네릭에 익숙하지 않으므로 많은 경우에 올바른 유형 매개 변수를 제공 할 수 없습니다. 이제 컴파일러는 자동으로 제네릭의 매개 변수 유형을 유추 하여이 상황을 줄이고 코드 가독성을 향상시킬 수 있습니다.
3. Java 7의 제네릭 유형 도출 개선 7
이전 버전의 Java 7에서 일반 유형을 사용하려면 값을 선언하고 할당 할 때 양쪽에 일반 유형을 추가해야합니다. 예를 들어:
Map <String, Integer> map = new Hashmap <String, integer> ();
많은 사람들이 처음에는 나와 동일했을 것입니다. 그들은 이것에 당황했습니다. 변수 선언에서 매개 변수 유형을 선언하지 않았습니까? 객체가 초기화 될 때 왜 여전히 글을 써야합니까? 이것은 또한 제네릭이 처음 등장했을 때 불만을 제기하는 것입니다. 그러나 Java가 개선되는 동안 디자이너는 끊임없이 Java 컴파일러를 개선하여보다 지능적이고 인간화되도록 만족합니다. 오늘날의 주인공은 다음과 같습니다. 유형 푸시 다운 ... 음 ... 유형 파생, 즉 유형 추론이 아닙니다. 이 사람이 나타날 때, 위의 코드를 작성하면 객체 인스턴스화가 인스턴스화 될 때 매개 변수 유형을 행복하게 생략 할 수 있으며 다음과 같이됩니다.
Map <String, Integer>지도 = New Hashmap <> ();
이 명령문에서 컴파일러는 변수 선언시 일반 유형을 기준으로 HashMap 인스턴스화 할 때 일반 유형을 자동으로 추론합니다. 다시 한 번, new HashMap 뒤의 "<>"에주의를 기울이십시오. 이 "<>"을 추가 함으로써만 자동 유형의 추론임을 의미하며, 그렇지 않으면 비 게니 릭 HashMap 이며 컴파일러를 사용하여 소스 코드를 컴파일 할 때 경고 프롬프트가 제공됩니다. 이 각도 브래킷 "<>"은 공식 문서에서 "다이아몬드"라고합니다.
그러나이 시점의 유형 도출은 완전하지 않습니다 (반제품 조정 제품조차도). Java SE 7에서 일반 인스턴스를 생성 할 때 유형 추론은 제한적이기 때문입니다. 컨텍스트에서 생성자의 파라미터 유형이 크게 선언 된 경우에만, 요법 추론이 사용될 수 있습니다. 그렇지 않으면 작동하지 않습니다. 예를 들어 : Java 7에서 다음 예제를 올바르게 컴파일 할 수는 없습니다 (그러나 메소드 파라미터를 기반으로 일반 유형이 자동으로 추론되므로 Java 8에서 컴파일 될 수 있습니다.
list <string> list = new arraylist <> (); list.add ( "a"); // addall이 유형 컬렉션의 매개 변수를 얻을 것으로 기대하기 때문에 <? string>, 다음 문은 list.addall (new arraylist <> ())를 전달할 수 없습니다.
4. Java 8의 재사용
최신 공식 Java 문서에서는 유형 파생의 정의를 볼 수 있습니다.
Type Insterence는 Java 컴파일러가 각 메소드 호출 및 해당 선언을 살펴 보는 능력으로 호출을 적용 할 수있는 유형 인수 (또는 인수)를 결정합니다. 추론 알고리즘은 인수의 유형과 사용 가능한 경우 결과가 할당되거나 반환되는 유형을 결정합니다. 마지막으로, 추론 알고리즘은 모든 인수와 함께 작동하는 가장 구체적인 유형을 찾으려고합니다.
요컨대, 유형 파생은 컴파일러가 호출하는 메소드와 해당 선언에 따라 필요한 매개 변수 유형을 결정하는 능력을 나타냅니다. 그리고 공식 문서에 다음을 설명하기위한 예제도 제공됩니다.
static <t> t pick (t a1, t a2) {return a2; } serializable s = pick ( "d", new arraylist <string> ()); 여기서, 컴파일러는 pick 방법에 전달 된 두 번째 매개 변수의 유형이 Serializable 하다는 것을 추론 할 수 있습니다.
이전 Java 버전에서 위의 예를 컴파일 할 수있는 경우 다음에 작성해야합니다.
Serializable s = this. <serializable> pick ( "d", new arraylist <string> ());
이 글을 쓰는 상세한 이유는 Bruce Eckel의 Java 프로그래밍 사고 (4 판)의 일반 장에서 볼 수 있습니다. 물론이 책은 Java 6을 기반으로 하며이 버전에는 유형 파생 개념이 없습니다. 이것을보고, 많은 사람들이 최신 버전에서 유형 파생의 힘을 명확하게 볼 수 있습니다. 일반 클래스의 선언 및 인스턴스화 프로세스에 더 이상 제한되지 않지만 일반 매개 변수가있는 메소드로 확장됩니다.
4.1 단용 추론 및 일반 방법
새 버전의 유형 파생 및 일반 방법과 관련하여 문서는 약간 더 복잡한 예를 제공합니다. 나는 그것을 여기에 게시했다. 원리는 위의 Serializable 예와 동일하므로 자세한 내용은 없습니다. 통합하려면 다음을 살펴볼 수 있습니다.
public class boxdemo {public static <u> void addbox (u u, java.util.list <u >> boxes) {box <u> box = new Box <> (); box.set (u); Box.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 + "는 [" + BoxContents.toString () + "]"); 카운터 ++; }} 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은 [10] Box #1이 포함되어 있습니다 [20] Box #2는 [30]에 포함됩니다.
일반 메소드 addBox 의 초점은 새로운 Java 버전의 메소드 호출에 더 이상 표시 할 필요가없는 유형 설명입니다.
BoxDemo. <integer> addbox (integer.valueof (10), listofintegerboxes);
컴파일러는 매개 변수 유형이 addBox 로 전달 된 매개 변수의 Integer 라고 자동으로 추론 할 수 있습니다.
4.2 제네릭 및 비 게 니체 클래스의 단위 추론 및 일반 생성자
글쎄 ... 이것은 영어로 된 더 나은 문장 일 수 있습니다 : 제네릭 및 비 게릭 클래스의 유형 추론 및 일반 생성자
실제로 일반 생성자는 일반 클래스의 특허가 아닙니다. 비 게 니체 클래스에는 고유 한 일반 생성자가있을 수 있습니다. 이 예를 살펴보십시오.
클래스 myclass <x> {<t> myclass (t t) {// ...}}MyClass 클래스에 다음과 같은 인스턴스화가 이루어지면 :
새로운 myclass <integer> ( "")
자, 여기서 우리는 myclass의 매개 변수 유형 x가 Integer 을 보여줍니다. 생성자의 경우 컴파일러는 공식 매개 변수 t가 들어오는 String 객체 ( "")를 기반으로 String 추론합니다. 이것은 Java7 버전으로 구현되었습니다. Java8에서 어떤 개선이 이루어 졌습니까? Java8 이후, 우리는 다음과 같은 일반적인 생성자로 일반 클래스의 인스턴스화를 쓸 수 있습니다.
myclass <integer> myObject = new MyClass <> ( "");
예, 여전히 다이아몬드라고하는 각도 브래킷 (<>) 쌍으로 컴파일러가 공식 매개 변수 x가 Integer 이고 t가 String 자동으로 추론 할 수 있습니다. 이것은 생성자의 일반화가 있다는 것을 제외하고는 Map<String,String> 의 초기 예와 매우 유사합니다.
유형 파생은 통화 매개 변수 유형, 대상 유형 (곧 논의 될 예정) 및 리턴 유형 (반품이있는 경우)을 기반으로 만 파생 될 수 있으며 프로그램 후 일부 요구 사항을 기반으로 도출 할 수 없습니다.
4.3 대상 유형
앞에서 언급했듯이 컴파일러는 대상 유형에 따라 유형 파생을 수행 할 수 있습니다. 표현식의 대상 유형은 표현식이 나타나는 위치에 따라 컴파일러가 필요한 올바른 데이터 유형을 나타냅니다. 예를 들어,이 예 :
static <t> list <t> emptylist (); list <string> listone = collections.emptylist ();
여기에서 <string>은 대상 유형입니다. 여기에 필요한 것은 List<String> 이고 Collections.emptyList() returns List<T> 이므로 여기에 컴파일러는 String 이어야합니다. 이것은 Java 7과 8에서 괜찮습니다. 그러나 Java 7에서는 다음과 같은 상황에서 정상적으로 편집 할 수 없습니다.
void processStringList (list <string> stringList) {// process stringList} processStringList (collections.emptyList ());현재 Java7은 다음 오류 메시지를 제공합니다.
// List <boodt>는 <string> 목록으로 변환 할 수 없습니다
이유 : Collections.emptyList() List<T> 반환하고 여기서는 특정 유형이 필요하지만, 필요한 것은 String 이라는 메소드 선언에서 추론 할 수 없기 때문에 컴파일러는 Object 값을 제공합니다. 분명히 List<Object> List<String>. 따라서 Java7 버전에서는이 방법을 다음과 같이 호출해야합니다.
ProcessStringList (Collections. <string> emptylist ());
그러나 Java 8에서 대상 유형 개념의 도입으로 인해 컴파일러가 필요로하는 것은 List<String> (즉, 대상 유형)이라는 것이 명백하므로 컴파일러는 String 된 List<T> 의 t가 문자열이어야합니다 processStringList(Collections.emptyList()); 괜찮습니다.
목표 유형의 사용은 람다 표현식에서 가장 분명합니다.
요약
자, 위는 Java의 유형 파생에 대한 개인적인 통찰력입니다. 요약하면, 점점 더 완벽한 유형 도출은 자연스럽게 보이는 일부 유형 변환 작업을 완료하는 것이지만, 이러한 모든 작업은 개발자가이를 표시 할 수 있도록 자동 도출을 위해 컴파일러에 맡겨집니다. 이 기사의 내용이 Java를 배우는 모든 사람들에게 도움이되기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다.