Java의 Lambda 표현에 대한 나의 견해는 상당히 얽혀 있습니다.
내가 이런 식으로 생각합니다 : Lambda Expressions는 Java 프로그램의 읽기 경험을 줄입니다. Java 프로그램은 표현력이 뛰어난 적이 없습니다. 반대로, Java를 인기있게 만드는 요소 중 하나는 보안과 보수주의입니다. 초보자조차도주의를 기울이는 한 강력하고 관리하기 쉬운 코드를 작성할 수 있습니다. Lambda Expressions는 개발자에 대한 요구 사항이 상대적으로 높기 때문에 일부 유지 보수 어려움도 증가합니다.
내가 생각하는 또 다른 것은 코드 코드로서 언어의 새로운 기능을 배우고 받아 들여야한다는 것입니다. 독서 경험이 좋지 않아 표현력을 포기한다면 일부 사람들은 개성 표현조차 이해하기가 어렵다는 것을 알게됩니다. 언어도 발전하고 있으며 유지할 수없는 사람들은 자발적으로 남겨질 것입니다.
나는 남겨두고 싶지 않다. 그러나 내가 선택을해야한다면, 나의 결정은 여전히 상대적으로 보수적이었다. Java 언어로 Lambda를 사용할 필요는 없다 - 그것은 현재 Java 서클의 많은 사람들이 그것에 익숙하지 않게하고, 인건비를 증가시킬 것이다. 당신이 그것을 매우 좋아한다면, 당신은 Scala를 사용하는 것을 고려할 수 있습니다.
어쨌든, 나는 여전히 Lambda를 마스터하려고 노력하기 시작했습니다. 결국 직장에서 유지되는 일부 코드는 Lambda를 사용합니다 (나를 믿으십시오. 점차 제거 할 것입니다). 학습 자습서는 공식 Oracle Java 웹 사이트의 관련 자습서입니다.
- - - - - - - - - - - - -
소셜 네트워크 응용 프로그램이 현재 생성되고 있다고 가정합니다. 한 가지 기능은 관리자가 메시지 보내기와 같이 지정된 기준을 충족하는 회원에게 특정 조치를 수행 할 수 있다는 것입니다. 다음 표는이 사용 사례를 자세히 설명합니다.
| 필드 | 설명하다 |
| 이름 | 수행 할 작업 |
| 주요 참가자 | 관리자 |
| 전제 조건 | 관리자 시스템에 로그인합니다 |
| 조건 후 | 지정된 기준을 충족하는 회원에 대한 조치 만 수행합니다. |
| 주요 성공 시나리오 | 1. 관리자는 대상 멤버가 작업을 수행하도록 필터링 표준을 설정합니다. 2. 관리자는 수행 할 조치를 선택합니다. 3. 관리자가 제출 버튼을 클릭합니다. 4. 시스템은 지정된 기준을 충족하는 구성원을 찾습니다. 5. 시스템은 지정된 기준을 충족하는 멤버에 대해 사전 선택된 작업을 수행합니다. |
| 펼친 | 실행 작업을 선택하거나 제출 버튼을 클릭하기 전에 관리자는 필터링 기준을 충족하는 멤버 정보를 미리보기 여부를 선택할 수 있습니다. |
| 발생 빈도 | 하루에 여러 번 발생합니다. |
다음 사람 클래스를 사용하여 소셜 네트워크에서 회원 정보를 나타냅니다.
공개 계급 사람 {public enum sex {Male, Female} 문자열 이름; 지역화 생일; 성별; 문자열 emailaddress; public int getage () {// ...} public void printperson () {// ...}}모든 멤버가 목록 <person> 인스턴스에 저장되었다고 가정합니다.
이 섹션에서는 매우 간단한 방법으로 시작한 다음 로컬 클래스와 익명 수업을 사용하여 구현하려고 노력하며 결국 Lambda 표현의 힘과 효율성을 점차적으로 경험할 것입니다. 전체 코드는 여기에서 찾을 수 있습니다.
솔루션 1 : 지정된 기준을 하나씩 만나는 멤버를 찾는 방법을 만듭니다.
이것은 위에서 언급 한 사례를 구현하기위한 가장 간단하고 거친 솔루션입니다. 여러 방법을 생성하는 것이며 각 방법은 기준 (예 : 연령 또는 성별)을 검증합니다. 다음 코드는 연령이 지정된 값보다 오래되었음을 확인합니다.
public static void printpersonsolderthan (list <person> 명단, int age) {for (person p : 명단) {if (p.getage ()> = age) {p.printperson (); }}}이것은 매우 깨지기 쉬운 솔루션이며, 약간의 업데이트로 인해 응용 프로그램이 실행되지 않을 가능성이 높습니다. 개인 클래스에 새로운 멤버 변수를 추가하거나 표준에서 나이를 측정하기위한 알고리즘을 변경하면이 변경에 적응하기 위해 많은 코드를 다시 작성해야합니다. 또한 여기의 제한이 너무 단단합니다. 예를 들어, 지정된 값보다 젊은 회원을 인쇄하려면 어떻게해야합니까? 또 다른 새로운 방법을 추가하여 printpersonsyoungerthan? 이것은 분명히 어리석은 방법입니다.
해결책 2 :보다 일반적인 방법을 만듭니다
다음 방법은 printpersonsolderthan보다 더 적응할 수 있습니다. 이 방법은 지정된 연령 그룹 내에서 회원 정보를 인쇄합니다.
public static void printpersonswithinagerange (list <person> 명단, int low, int high) {for (person p : 명단) {if (low <= p.getage () && p.getage () <high) {p.printperson (); }}}이제 새로운 아이디어가 있습니다. 지정된 성별에 대한 회원 정보를 인쇄하거나 지정된 성별을 충족하고 지정된 연령 그룹 내에있는 경우 어떻게해야합니까? 사람 클래스를 조정하고 우정 및 지리적 위치와 같은 속성을 추가하면 어떻게해야합니까? 이와 같은 쓰기 방법은 printpersonsyoungerthan보다 보편적이지만 가능한 모든 쿼리에 대한 방법을 작성하면 코드의 취약성이 발생할 수 있습니다. 표준 점검 코드를 새로운 클래스에 넣는 것이 좋습니다.
솔루션 3 : 로컬 클래스에서 표준 검사를 구현하십시오
다음 방법은 검색 기준을 충족하는 회원 정보를 인쇄합니다.
public static void printpersons (list <person> 명단, 검사원 테스터) {for (person p : 명단) {if (treader.test (p)) {p.printperson (); }}}검사기 객체 테스터는 프로그램에서 목록 매개 변수 명단에서 각 인스턴스를 확인하는 데 사용됩니다. treader.test ()가 true를 반환하면 printperson () 메소드가 실행됩니다. 검색 기준을 설정하려면 검사원 인터페이스를 구현해야합니다.
다음 클래스는 점검원을 구현하고 테스트 방법의 구체적인 구현을 제공합니다. 이 클래스의 시험 방법은 미국의 군 복무 요구 사항을 충족하는 회원 자격, 즉 남성 성별과 18 세에서 25 세 사이의 정보를 필터링합니다.
클래스 checkpersoneligibleforselectiveService checkperson {public boolean test (person p) {return p.gender == person.sex.male && p.getage ()> = 18 && p.getage () <= 25; }}이 클래스를 사용하려면 인스턴스를 만들고 printpersons 메소드를 트리거해야합니다.
PrintPersons (로스터, 새로운 CheckpersonEleigibleForselectiveService ());
코드는 이제 깨지기 쉬운 것처럼 보입니다. 개인 클래스 구조의 변경으로 인해 코드를 다시 작성할 필요가 없습니다. 그러나 여기에는 여전히 추가 코드가 있습니다. 응용 프로그램의 각 검색 표준에 대해 내부 클래스를 정의하는 새로 정의 된 인터페이스입니다.
CheckpersonELigibleForSelectiveService는 인터페이스를 구현하기 때문에 각 표준의 내부 클래스를 정의하지 않고 익명 클래스를 사용할 수 있습니다.
솔루션 4 : 익명 클래스를 사용하여 표준 검사를 구현하십시오
아래라는 PrintPersons 메소드의 매개 변수 중 하나는 익명 클래스입니다. 이 익명 클래스의 기능은 반응식 3의 CheckpersoneligibleForselectives 서비스 클래스의 기능과 동일합니다. 모두 남성 성별을 가진 필터링 된 구성원이며 18 세에서 25 세 사이의 연령입니다.
PrintPersons (로스터, New Checkperson () {public boolean test (person p) {return p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25;}});이 체계는 더 이상 각 검색 체계에 대한 새로운 클래스를 생성 할 필요가 없기 때문에 인코딩의 양을 줄입니다. 그러나이를 수행하는 것은 여전히 불편합니다. 수표 인터페이스에는 하나의 메소드 만 있지만 구현 된 익명 클래스는 여전히 약간 장악하고 부피가 커집니다. 현재 Lambda Expression을 사용하여 익명 클래스를 대체 할 수 있습니다. 다음은 Lambda 표현을 사용하여 익명 클래스를 대체하는 방법을 설명합니다.
솔루션 5 : Lambda 표현식을 사용하여 표준 점검을 구현하십시오
Checkperson 인터페이스는 기능 인터페이스입니다. 소위 기능 인터페이스는 하나의 추상 방법 만 포함하는 모든 인터페이스를 나타냅니다. (기능적 인터페이스에는 여러 기본 메소드 또는 정적 메소드가있을 수 있습니다). 기능 인터페이스에는 하나의 추상 메소드가 있기 때문에이 기능 인터페이스의 메소드를 구현할 때 메소드의 메소드 이름을 생략 할 수 있습니다. 이 아이디어를 구현하려면 익명 클래스 표현식을 Lambda 표현으로 바꿀 수 있습니다. 아래에 다시 작성된 Printpersons 메소드에서 관련 코드가 강조 표시됩니다.
printpersons (명단, (person p) -> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25);
여기서 표준 기능 인터페이스를 사용하여 Checkperson 인터페이스를 교체하여 코드를 추가로 단순화 할 수 있습니다.
솔루션 6 : Lambda 표현식에서 표준 기능 인터페이스를 사용하십시오
Checkerson 인터페이스를 살펴 보겠습니다.
인터페이스 검사원 {부울 테스트 (Person P); }이것은 매우 간단한 인터페이스입니다. 추상적 인 방법은 하나만 있기 때문에 기능 인터페이스이기도합니다. 이 추상 메소드는 하나의 매개 변수 만 허용하고 부울 값을 반환합니다. 이 추상 인터페이스는 매우 간단하여 응용 프로그램에서 그러한 인터페이스를 정의 해야하는지 여부를 고려할 것입니다. 현재 JDK로 정의 된 표준 기능 인터페이스를 사용하는 것을 고려할 수 있으며 Java.util.Function 패키지에서 이러한 인터페이스를 찾을 수 있습니다.
이 예에서는 술어 <T> 인터페이스를 사용하여 Checkperson을 대체 할 수 있습니다. 이 인터페이스에는 부울 테스트 (t t) 메소드가 있습니다.
인터페이스 술어 <t> {부울 테스트 (t t); }술어 <t> 인터페이스는 일반적인 인터페이스입니다. 일반 클래스 (또는 일반 인터페이스)는 한 쌍의 각도 브래킷 (<>)을 사용하여 하나 이상의 유형 매개 변수를 지정합니다. 이 인터페이스에는 하나의 유형 매개 변수 만 있습니다. 구체적인 클래스를 사용하여 일반 클래스를 선언하거나 인스턴스화하면 매개 변수화 된 클래스가 나타납니다. 예를 들어, 매개 변수화 된 클래스 술어 <사람>은 다음과 같습니다.
인터페이스 술어 <person> {부울 테스트 (person t); }이 매개 변수화 된 클래스에는 Checkperson.boolean test (Person P) 메소드의 매개 변수 및 반환 값과 일치하는 메소드가 있습니다. 따라서 다음 방법에서 설명 된대로 술어 <T> 인터페이스를 사용하여 Checkperson 인터페이스를 대체 할 수 있습니다.
public static void printpersonswithPredicate (list <person> 명단, 술어 <사람> 테스터) {for (person p : 명단) {if (tester.test (p)) {p.printperson (); }}}그런 다음 다음 코드를 사용하여 계획 3에서와 같이 군 복무원을 필터링하십시오.
printpersonswithPredicate (로스터, p-> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25);
Percticate <person>을 매개 변수 유형으로 사용할 때 명시 적 매개 변수 유형이 지정되지 않음을 알 수 있습니다. Lambda 표현이 적용되는 유일한 곳은 아닙니다. 다음 체계는 Lambda 표현의 더 많은 사용을 소개합니다.
솔루션 7 : 응용 프로그램 전체에서 Lambda 표현식을 사용하십시오
PredPersonswithpredicate 메소드를 살펴보고 Lambda 표현식을 여기에서 사용할 수 있는지 고려해 봅시다.
public static void printpersonswithPredicate (list <person> 명단, 술어 <사람> 테스터) {for (person p : 명단) {if (tester.test (p)) {p.printperson (); }}}이 방법에서는 명단의 각 개인 인스턴스가 술어 인스턴스 테스터를 사용하여 점검됩니다. 개인 인스턴스가 테스터에 정의 된 체크 기준을 준수하면 개인 인스턴스의 프린터 메소드가 트리거됩니다.
프린터 메소드를 트리거하는 것 외에도 테스터 표준을 충족하는 개인 인스턴스도 다른 방법을 실행할 수 있습니다. Lambda 표현식을 사용하여 실행되는 방법을 지정하는 것을 고려할 수 있습니다 (이 기능은 양호하다고 생각합니다. 이는 Java의 메소드를 객체로 전달할 수없는 문제를 해결합니다). 이제 Printperson 메소드와 유사한 Lambda 표현이 필요합니다. 하나의 매개 변수 만 필요하고 void를 반환하는 Lambda 표현식입니다. 한 가지를 기억하십시오 : Lambda 표현식을 사용하려면 먼저 기능 인터페이스를 구현해야합니다. 이 예에서는 하나의 추상 방법 만 포함하는 기능 인터페이스가 필요합니다. 이 추상 방법에는 유형 사람의 매개 변수가 있으며 무효로 돌아갑니다. JDK가 제공 한 표준 기능 인터페이스 소비자 <t>를 살펴볼 수 있습니다.이 요구 사항은이 요구 사항을 충족합니다. 다음 코드에서 소비자 <t> 인스턴스를 사용하여 p.printperson () 대신 수락 메소드를 호출하십시오.
public static void processpersons (list <person> 명단, 술어 <사람> 테스터, 소비자 <person> block) {for (person p : 명단) {if (tester.test (p)) {block.acplect (p); }}}이에 따라 다음 코드를 사용하여 군 복무 연령의 구성원을 필터링 할 수 있습니다.
ProcessPersons (로스터, p-> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25, p -> p.printperson ());
회원 정보를 인쇄 할뿐만 아니라 멤버십 확인, 회원 연락처 정보 받기 등과 같은 더 많은 작업을 수행하려면이 시점에서 반환 값 메소드가있는 기능 인터페이스가 필요합니다. JDK의 표준 기능 인터페이스 함수 <t, r>에는이 r apply (t t)와 같은 메소드가 있습니다. 다음 방법은 매개 변수 맵퍼의 데이터를 가져 오고이 데이터의 매개 변수 블록에서 지정된 동작을 수행합니다.
public static void processpersonswithfunction (list <person> 로스터, 술어 <사람> 테스터, 기능 <person, string> mapper, consumer <string> block) {for (person p : roster) {if (tester.test (p)) {String data = mapper.apply (p); block.cept (데이터); }}}다음 코드는 명단에서 군 복무 연령의 모든 구성원의 이메일 정보를 얻고 다음과 같이 인쇄합니다.
ProcessPersonSwithFunction (로스터, p-> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25, p-> p.geteMailAddress (), 이메일 -> System.out.println (이메일));
해결책 8 : 제네릭을 더 자주 사용하십시오
프로세스 폰서와의 기능을 검토해 봅시다. 다음은이 방법의 일반 버전입니다. 새로운 방법은 매개 변수 유형에서 더 많은 공차가 필요합니다.
public static <x, y> void ProcessElements (ITERABLE <x> 소스, 술어 <x> 테스터, 기능 <x, y> 맵퍼, 소비자 <y> 블록) {for (x p : source) {if (tester.test (p)) {y data = mapper.apply (p); block.cept (데이터); }}}올바른 나이에 군 복무에 대한 회원 정보를 인쇄하려면 다음과 같은 프로세스 방법을 호출 할 수 있습니다.
ProcessElements (로스터, p-> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25, p-> p.geteMailAddress (), 이메일 -> system.out.println (이메일));
메소드 통화 프로세스 중에 다음 동작이 수행됩니다.
컬렉션에서 객체 정보를 얻으십시오.이 예에서는 컬렉션 인스턴스 명단에서 사람 객체 정보를 얻습니다.
술어 인스턴스 테스터와 일치 할 수있는 필터 객체. 이 예에서, 술어 대상은 적절한 나이에 군 복무를 걸러내는 조건을 지정하는 람다 표현입니다.
필터링 된 객체는 처리를 위해 함수 객체 맵퍼로 넘겨지고 맵퍼는이 객체의 값과 일치합니다. 이 예에서 기능 객체 맵퍼는 각 멤버의 이메일 주소를 반환하는 람다 표현식입니다.
Mapper와 일치하는 값에 대한 소비자 객체 블록의 동작을 지정합니다. 이 예에서 소비자 객체는 Lambda 표현식이며, 이는 문자열 인쇄 기능이며, 이는 기능 인스턴스 매퍼가 반환 한 회원 이메일 주소입니다.
솔루션 9 : Lambda Expression을 사용하여 매개 변수로 집계 작업 사용
다음 코드는 집계 작업을 사용하여 명단 컬렉션에서 군대 회원의 이메일 주소를 인쇄합니다.
Roster.stream () .filter (p-> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25) .map (p-> p.getemailaddress ()) .foreach (이메일 -> system.out.println (이메일));
위 코드의 실행 프로세스를 분석하고 다음 표를 구성하십시오.
행동 | 집계 작업 |
물체를 얻으십시오 | 스트림 <e> 스트림 () |
술어 인스턴스의 지정된 기준과 일치하는 필터 | 스트림 <t> 필터 (Predicate <? Super T> Predict) |
함수 인스턴스를 통해 객체의 일치하는 값을 얻습니다. | <r> stream <r> map (function <? super t,? extends r> 맵퍼) |
소비자 인스턴스에 의해 지정된 동작을 실행합니다 | void foreach (소비자 <? super t> action) |
테이블의 필터, 맵 및 사례 작업은 모두 집계 작업입니다. 집계 작업에 의해 처리 된 요소는 컬렉션에서 직접가 아니라 스트림에서 나옵니다 (즉,이 예제 프로그램에서 호출 된 첫 번째 방법은 Stream ()이기 때문에). 스트림은 데이터 시퀀스입니다. 컬렉션과 달리 Stream은 데이터를 특정 구조로 저장하지 않습니다. 대신 Stream은 컬렉션과 같은 특정 소스에서 파이프 라인을 통해 데이터를 가져옵니다. 파이프 라인은 스트림 작동 시퀀스이며,이 예에서 필터 맵 포르설입니다. 또한 집계 작업은 일반적으로 Lambda 표현식을 매개 변수로 사용하여 많은 사용자 정의 공간을 제공합니다.