2013 년에 발표 된 Javase8에는 올해 6 월 JSR-335 초안에 설명 된 Lambda Project라는 계획이 포함됩니다.
JSR-335는 폐쇄를 Java에 도입했습니다. 폐쇄는 C ++, C#과 같은 많은 인기있는 언어로 존재합니다. 클로저를 사용하면 기능 포인터를 만들어 매개 변수로 전달할 수 있습니다. 이 기사에서는 Java8의 특성을 대략보고 Lambda 표현을 소개합니다. 그리고 나는 몇 가지 개념과 문법을 설명하기 위해 샘플 프로그램을 설치하려고 노력할 것입니다.
Java 프로그래밍 언어는 인터페이스의 개념을 제공하며 추상 방법은 인터페이스에 정의 될 수 있습니다. 인터페이스는 API를 정의하고 사용자 또는 공급 업체가 이러한 방법을 구현하기를 희망합니다. 여러 번, 우리는 일부 인터페이스에 대한 독립적 인 구현 클래스를 만들지 않습니다.
익명 사용이 널리 사용됩니다. 익명의 내부 클래스에서 사용되는 가장 일반적인 장면은 이벤트 프로세서입니다. 둘째, 익명의 내부 클래스는 종종 다중 스레드 프로그램에 사용됩니다.
우리가 논의하는 것처럼, 익명 클래스는 닌자의 주어진 인터페이스를 구현하는 것입니다. 일반적 으로이 구현 클래스의 객체를 메소드로 매개 변수로 전달한 다음이 메소드는 구현 클래스 메소드를 내부적으로 구현 클래스로 호출합니다. 따라서이 인터페이스를 콜백 인터페이스라고합니다.
익명 범주는 어디에서나 사용되지만 여전히 많은 문제가 있습니다. 첫 번째 주요 문제는 복잡성입니다. 이 클래스는 코드의 수준을 수직 privem이라고도하는 지저분하고 복잡하게 보이게합니다. 둘째, 패키징 클래스의 비 -기본 구성원에게는 액세스 할 수 없습니다. 이것의 키워드는 매우 혼란스러워 질 것입니다. 익명 클래스의 패키징 클래스와 동일한 멤버 이름이있는 경우 내부 변수는 외부 멤버 변수를 포함합니다. 이 키워드는 그의 캡슐화 객체보다는 익명의 객체 자체가 가치가 있기 때문입니다.
public void anonymousexample () {string nonfinalvariable = "nonformal exmple"; "OUTER METHOD VARIBLE () {String variable ="runnable class run () {String variable = " 실행 방법 "; // 아래 줄은 컴파일 오류를 제공합니다. //system.out.println ("-> "" + nonfinalVariable); println ( "->" + this.variable);}}). 출력은 다음과 같습니다.
-> 실행 메소드 varial-> runnable 클래스 멤버
이 예는 위에서 언급 한 문제를 설명하고 Lambda Expression은 익명의 내부 클래스로 인한 모든 문제를 거의 해결합니다. Lambda 표현을 더 탐색하기 전에 기능적 인터페이스를 살펴 보겠습니다.
기능적 인터페이스
기능 인터페이스는이 메소드 계약을 나타내는 단일 메소드 만있는 인터페이스입니다.
위의 정의 중 하나만 그렇게 간단하지 않습니다. 나는이 단락을 이해하지 못한다. 독자들은 원래의 텍스트를 단일 방법을 억압해야한다.
다음 예는 기능 인터페이스의 개념을 이해하는 방법을 명확하게 보여줍니다.
인터페이스 runnable {void run ();} // functionalinterface foo {boolean equals (object obj);} // equals는 암시 적 인터페이스 막대가 foo (string o1, string o2);} //입니다. 기능적; 막대는 비 객체의 비교 (객체 obj)를 가지고있다. 기능적이지 않음; 메소드 메소드. 같은 서명대부분의 콜백 인터페이스는 기능적 인터페이스입니다. 예를 들어, 런 가능, 호출 가능, 비교기 등 이전에 Sam (Single Abstract Method)이라고 불 렸습니다.
람다 표현
우리가 말했듯이, 익명 카테고리의 주요 문제는 코드의 수준이 지저분 해 보인다는 것입니다. 람다 표현은 방법처럼 보입니다. 공식 매개 변수 목록과 이러한 매개 변수의 차단이 있습니다.
(String S) -> S.lengh;
위의 예는 첫 번째 표현식이 매개 변수로 문자열 변수를 수신 한 다음 문자열의 길이를 반환한다는 것을 의미합니다. 매개 변수가없는 두 번째는 43입니다. 마지막으로, 세 번째는 두 개의 정수 X와 Y를 받아들이고 평화로 돌아 왔습니다.
많은 단어를 읽은 후 마지막으로, 나는 첫 번째 Lambda 표현의 예를 제공 할 수 있습니다.
공개 클래스 FirstLambdaexpression {public string variable = "클래스 레벨 변수"; string nonfinalvariable = "이것은 최종 변수입니다"; 새 스레드 (() -> {// belw line은 컴파일 오류 // String variable = "run method variable"system.out. println ( " ->" " + 변수). ; 출력은 다음과 같습니다.
-> 메소드 로컬 변수-> 클래스 레벨 변수
Lambda Expressions와 익명의 내부 클래스 사용의 차이를 비교할 수 있습니다. Lambda Expression을 사용하여 익명 범주를 작성하면 가변 가시성 문제가 해결된다고 말할 수 있습니다. 코드의 주석을 살펴보면 적용 범위 변수를 생성 할 수 없습니다.
일반적인 Lambda Expression Syntax에는 매개 변수 목록이 포함되어 있으며 화살표 키워드 "->"은 본체입니다. 주제는 표현식 (One -Line 문) 또는 다중 라인 문장 일 수 있습니다. 표현식이라면 계산되고 반환됩니다. 중단 및 계속은주기 내에서만 사용할 수 있습니다.
이 스타일은 일반적으로 C#과 Scala에 있기 때문에이 특별한 문법 형태를 선택하는 이유는 무엇입니까? 이 문법 디자인은 기본적으로 익명 유형의 복잡성을 해결합니다. 그러나 동시에, 그는 예를 들어, 방법이 단일 표현이라면 괄호와 반환 문은 불필요합니다. 표현의 결과는 자신의 반환 값입니다. 이 유연성은 코드를 간단하게 유지할 수 있습니다.
Lambda 표현은 익명으로 사용되므로 다른 모듈 또는 다른 Lambda 표현 (중첩 Lambda 표현)에서 유연하게 사용할 수 있습니다.
// 람다 박람회는 메소드 매개 변수 블록으로 둘러싸여 있습니다 .//target 인터페이스 유형은 tproperty ( "propname")입니다. (() -> {System.out.println ( "다른 스레드에서 실행");};
Lambda Expression을 자세히 살펴보면 대상 인터페이스 유형이 표현식의 일부가 아니라는 것을 알 수 있습니다. 컴파일러는 람다 표현식의 유형과 주변 환경을 추론하는 데 도움이됩니다.
Lambda 표현식에는 대상 유형이 있어야하며 가능한 모든 대상 유형에 적응할 수 있습니다. 대상 유형이 인터페이스 인 경우 다음 조건을 충족하여 올바르게 컴파일해야합니다.
컴파일러는 대상 유형 문을 통해 매개 변수 유형과 숫자를 알 수 있기 때문에 Lambda 표현식에서 매개 변수 유형 선언을 생략 할 수 있습니다.
비교기 C = (S1, S2) -> S1.comparetoignorecase (S2);
또한 대상 유형에서 선언 된 메소드가 하나의 매개 변수 만 수신되면 (여러 번이 경우에도) 매개 변수의 작은 괄호도 다음과 같이 쓸 수 있습니다.
ActionListEnr Listenr = event-> event.getWhen ();
매우 명백한 질문이옵니다. Lambda는 왜 지정된 메소드 이름이 아닌가?
대답은 : Lambda 표현식은 기능 인터페이스에만 사용될 수 있지만 기능적 인터페이스에는 하나의 방법이 있습니다.
Lambda 표현식을 만들기위한 기능 인터페이스를 결정할 때 컴파일러는 기능적 인터페이스 중국 법의 서명을 인식하고 주어진 표현식이 일치하는지 확인할 수 있습니다.
이 유연한 문법은 익명의 수직 privem 사용을 피하는 데 도움이되며 수평 개인 (매우 긴 일회용 문장)을 가져 오지 않습니다.
Lambda Expression Grammar는 컨텍스트와 관련이 있지만 처음은 아닙니다. Java SE 7에 의해 추가 된 다이아몬드 운영자 도이 개념을 가지고 있으며,이 개념은 컨텍스트에 의해 추론됩니다.
void invoke (runnable r) {r.run ()} void future invoke (callable r) {return c.compute ()} // 위의 두 가지 방법 onal interfacefuture s = invoke (() -> "done"); / 어떤 호출이 호출됩니까?
위의 질문에 대한 답은 호출 가능한 매개 변수를 수신하는 방법을 호출하는 것입니다. 이 경우 컴파일러는 다양한 매개 변수 유형의로드를 통해 해결됩니다. 적용 가능한 로딩 방법이 둘 이상인 경우 컴파일러는 Lambda 표현 및 해당 대상 유형의 호환성을 확인합니다. 간단히 말해, 위의 호출 방법은 반환 될 것으로 예상되지만 하나의 호출 방법 만 반환 값을 갖습니다.
Lambda 표현식은 해당 유형과 호환되는 한 지정된 대상 유형으로 명시 적으로 변환 될 수 있습니다. 다음 프로그램을 살펴보면 세 가지 유형의 호출 가능성을 구현했으며 모두 클래스 유형으로 변환했습니다.
공개 클래스 FirstWithLambDaexpressions {public static void main (String [] args) {listl = arrayslist (callial)-> "Callable 1", (Call able) ()-> "Callable 2"(Callable)-> "Callable 3"; ) {e1.printstacktrace ();} e.shutdown ();} public void dumplist (목록)는 interuptexception, executionExcrecti를 {for (future : list) {system.out.println (future.get ()); }}}앞에서 논의한 바와 같이, 익명 범주는 주변 환경의 비 기본 변수에 액세스 할 수 없습니다. 그러나 람다 표현에는 그러한 한계가 없습니다.
현재 정의 된 기능 인터페이스는 인터페이스에만 적용됩니다. 나는 하나의 추상적 인 방법의 람다 표현식을 만들려고했지만 컴파일 오류가 이루어졌다. JSR -335에 따르면, 미래 버전의 Lambda Expression은 기능적 클래스를 지원할 수 있습니다.
메소드 견적
메소드를 호출하지 않고 참조 방법으로 참조했습니다.
Lambda Expression을 사용하면 익명 방법을 정의하여 기능 인터페이스의 인스턴스로 사용할 수 있습니다. 방법은 Lambda 표현식과 매우 유사하지만, 그 차이는 메소드를 제공하지 않는 메소드의 구현을 인용한다는 것입니다.
System :: GetProperty "ABC":: Playstring :: Specipper :: TostringArraylist :: New
위의 진술은 방법의 일반적인 구문과 생성자에 대한 참조를 보여줍니다. 여기서 우리는 새로운 운영 캐릭터를 보았습니다 ":::: double Colon).이 연산자로 정확한 이름을 모르지만 JSR은이를 분리기라고 말하며 Wikipedia 페이지는이를 스코프 분석 작업이라고합니다. . 참조로서,이 튜토리얼의 범위 내에서, 우리는 그것을 분리 주의자로 사용할 것입니다.
대상 참조 또는 수신기는 제공자 및 분리기 뒤에 배치됩니다. 이것은 방법을 인용 할 수있는 표현을 형성합니다. 마지막 진술에서, 메소드의 이름은 "새"입니다. 이 표현식은 Arraylist 클래스의 구조 방법을 인용합니다 (다음 섹션의 생성자에 대한 참조).
이것을 이해하기 전에, 나는 당신이 인용 된 방법의 강점을 보도록하고 싶습니다.
java.util.arrays; = {신입 사원 ( "Nick"), 신입 사원 ( "Robin"), 신입 사원 ( "Josh"), 신입 사원 ( "Mark")}; 정렬 전 : "); dumpemployee (직원); Arrays.Sort (직원, Employeee :: myCompare); System.out.println ("정렬 : "); Mloyees); 직원) {for (Employee emp : Arrays. ASLIST (직원)) {System.out.print (Emp.Name+",");} system.out.println (string name) {this.name = name;} public static int mycompare. (Employeee emp1, Employee emp2) {return emp1.name.compareto (emp2.name);}} 출력은 다음과 같습니다.
정렬 전에 : Nick, Robin, Josh, Andy, Mark, 앤디, 조쉬, 마크, 닉, 로빈,
출력은 특별하지 않습니다. 정적 메소드 MyCompare는 두 개의 직원 개체를 수신하고 비교할 이름을 반환합니다.
주요 방법에서는 다른 직원 배열을 만들어 배열로 전달했습니다. SORT 메소드를 표현식 배열 (Employee :: myCompare)에 참조하십시오.
잠깐만 요, Javadoc을 보면 정렬 방법의 두 번째 매개 변수는 Corarator 유형이지만 직원의 정적 메소드 참조를 전달합니다. 중요한 문제는 여기에 있습니다. 직원이 비교 가능한 인터페이스를 구현하지도 않고 독립적 인 비교기 클래스를 작성하지 않았지만 출력에는 아무런 문제가 없습니다.
이유를 살펴 보겠습니다. Arrays.sort 메소드는 비교기의 인스턴스를 기대 하고이 비교기는 기능적 인터페이스이므로 하나의 방법, 즉 비교 만 있음을 의미합니다. 여기서 우리는 또한 람다 표현을 악의적으로 통과 시켜이 표현에서 compaare 방법의 구현을 제공합니다. 그러나 우리에게는 직원 수업에 이미 비교 방법이 있습니다. 그들의 이름은 다르기 때문에 매개 변수의 유형 및 반환 값이 동일합니다.
동일한 이름의 여러 메소드가 있으면 컴파일러는 대상 유형에 따라 가장 잘 일치하는 것을 선택합니다. 이해하려면 예를 보려면 :
public static int mycompare (Employeee emp1, Employee emp2) {return emp1.name.compareto (emp2.name);} // 위와 같은 이름을 가진 다른 방법. {{) {{) {{return int1.compareto (int2);} 정렬을 위해 두 개의 다른 배열을 만들었습니다.
직원 [] 직원 = {신입 사원 ( "Nick"), 신입 사원 ( "Robin"), 신입 사원 ( "Josh"), 신입 사원 ( "Andy"), 신입 사원 ( "Mark")}; ins = {1, 4, 8, 2, 3, 8, 6}; 이제 다음 두 줄의 코드를 실행합니다
Arrays.SORT (직원 :: myCompare);
여기서는 두 줄의 코드가 동일합니다 (Employee :: myCompare).
정적 메소드에 의해 오해되지 마십시오. 예제 메소드에 대한 참조를 만들 수도 있습니다. 정적 메소드의 경우 클래스 이름 :: 메소드 이름을 사용하여 메소드 참조가 인스턴스 메소드를 참조하면 객체 :: 메소드 이름입니다.
위의 예는 상당히 좋지만 정수가 비교 가능하고 구현 방법 비교를 제공하기 때문에 정수 비교 방법을 작성할 필요는 없습니다. 따라서 다음 줄만 직접 사용합니다.
arrays.sort (ints, integer :: compareto);
이것을보고, 당신은 조금 혼란스러워합니까? 아니요? 그런 다음 여기에서 혼동하겠습니다. INTEGER는 New Integer ()와 같은 인스턴스가 아닙니다 회원 방법은 참조됩니다.
답은 다음과 같습니다.이 유형의 진술은 일부 특정 유형에서 사용할 수 있습니다. 정수는 데이터 유형이며 데이터 유형의 경우이 명령문이 허용됩니다.
직원 메소드 MyCompare를 비 static으로 바꾸면 다음을 사용합니다. Employee :: myCompare는 컴파일 오류가 있습니다. 적절한 방법이 없습니다.
건설적인 방법 참조
생성자 참조는 제도화없이 생성자를 언급하는 클래스로 사용됩니다.
건설적인 방법 참조는 Javase 8의 새로운 기능입니다. 건설적인 방법에 대한 참조를 구성하고이를 대상 유형으로 매개 변수로 전달할 수 있습니다.
우리가 그것을 참조 할 때, 우리는 그것들을 사용하기 위해 기존 방법 하나를 인용합니다. 마찬가지로, 건설적인 메소드 참조를 사용할 때, 우리는 기존 건설적인 방법에 대한 참조를 만듭니다.
이전 섹션에서, 우리는 메소드 참조처럼 보이는 생성자 :: new가 참조하는 문법 이름을 보았습니다. 이 구성 방법에 대한 참조는 대상 기능 인터페이스의 인스턴스에 할당 될 수 있습니다. 클래스에 여러 생성자가있을 수 있습니다.이 경우 컴파일러는 기능적 인터페이스의 유형을 확인하고 최상의 일치를 찾습니다.
저에게는 첫 번째 건설적인 방법을 쓰기가 어렵습니다. 결국, 나는 열심히 일하는 데 오랜 시간을 보냈고 마침내 "아, 찾았습니다 ...", 다음 절차를 보았습니다.
public constructor -references {public static void main (string [] ar) {myinterface in = myclass :: new; } 출력은 다음과 같습니다.
-> com.myclass@34e5307e
인터페이스에서 선언 된 메소드를 제외하고는이 클래스의 반환 값이 조금 놀랍습니다.
이 예는 내 마음의 또 다른 문제를 불러 일으켰습니다. 매개 변수로 건설적인 방법을 인스턴스화하는 방법은 무엇입니까? 아래 절차를보십시오.
공공 클래스 Constructor-References {public static void main (String [] ar) {Emlpoyeproder Pruess =: new ( "John", 30); ; Age) {this.name = 이름 ;age = age;}}} 출력은 다음과 같습니다.
-> 직원 이름 : John-> 직원 연령 : 30
이 기사를 읽기 전에 Javase8-Default 메소드에서 가장 멋진 기능을 살펴 보겠습니다.
기본 방법
Javase8은 기본 메소드라는 개념을 소개합니다. 인터페이스의 초기 Java 버전에는 매우 엄격한 인터페이스가 있습니다. 다가오는 Java 버전에서는 인터페이스에서 메소드의 기본 구현이 허용됩니다. 말도 안되는 말도 안돼 다음을보십시오.
public class defaultmethods {public static void main (string [] ar) {normal instance instance.mynormalmethod (); System.out.println ( "-> myDefaultMethod");}} class NormalInterfacempl emplemelemerInterface {@override public void mynormalmethod () {) system.out.println ( "-> mynormalmethod");}} 출력은 다음과 같습니다.
-> MyDefaultMethod
위의 인터페이스는 두 가지 메소드를 선언하지만이 인터페이스의 구현 클래스는 MyDefaultMethod가 기본 수정자를 사용하고 기본 구현을위한 메소드 블록을 제공하기 때문에 그 중 하나만 인식합니다. GM의 무거운 부하 규칙은 여전히 여기에서 적용됩니다. 구현 클래스가 인터페이스의 메소드를 구현하면 호출 할 때 통화 클래스의 메소드가됩니다. 그렇지 않으면 기본 구현이 호출됩니다.
통합 부모 인터페이스의 인터페이스는 상위 인터페이스의 기본 구현을 증가, 변경 및 제거 할 수 있습니다.
interface parentInterface {void infertIteriNomal (); -> 초기 이름);} void inittileDefault (); // 이제 정상 메소드} 이 예에서는 두 가지 방법이 정상적이며 다른 방법은 기본적으로 구현됩니다.
클래스가 클래스 C를 상속 받고 인터페이스 I을 깨달았으며 C에는 메소드가 있으며 기본 메소드가 제공하는 기본 메소드를 제공하는 메소드가 호환됩니다. 이 경우 C의 메소드는 I의 기본 메소드에 우선 순위를 부여하며 메소드가 추상적 일 때 C의 메소드조차도 여전히 우선 순위입니다.
public class defaultmethods {public static void main (string [] ar) {interfaxe impl = new empartmethodpl (); ;}} interface Interfaxe {public void defaultmethod () default {System.out.println ( "-> interfaxe"); 출력은 다음과 같습니다.
-> ParentClass
두 번째 예는 내 클래스가 두 개의 다른 인터페이스를 구현했지만 두 인터페이스는 모두 동일한 기본 구현 방법으로 동일한 문을 제공한다는 것입니다. 이 경우 컴파일러는 무슨 일이 일어나고 있는지 파악할 수 없습니다. 이것은 다음 방법으로 수행 할 수 있습니다.
public class defaultmethods {public static void main (string [] ar) {news normal interfampl (); );}} 인터페이스 SecondInterface {public void defaultMethod () default {System.out.println ( "-> SecondInterface"); 출력은 다음과 같습니다.
-> SecondInterface
이제 우리는 Java 폐쇄의 도입을 읽었습니다. 이 기사에서는 Java의 Lambda 표현, 메소드 참조 및 생성자 참조를 이해 한 기능 인터페이스 및 Java 폐쇄와 접촉했습니다. 그리고 우리는 또한 Lambda Expression의 Hello World 예를 썼습니다.
Javase8은 곧이 새로운 기능이 혼란 스러울 것입니다.