2023. 10. 18. 14:21ㆍ프로그래밍/Java
람다라는 표현은 프로그램을 처음 배울 때 얼핏 들은 기억이 있다.
그러나 척 보기에도 사용하기 어려워보이고 그 당시의 내 수준에서는 공부할 부분이 아니라 생각해 넘기곤 했다.
그러나 생각해보면 JDK별로 다른 버전을 제공하고 내가 왜 그 버전을 사용해야 하는지에 대한 고민은 거의 해보지 않았던 것 같다.
그냥 라이브러리와 여러 프레임워크간의 호환성만을 위해 언어의 버전을 반 강제적으로 좇았기 때문이다.
- 람다 표현식이란 무엇인가?
JDK8버전부터 추가된 Lambda는 Stream과도 관련이 있는 듯하다.
Oracle 공식 문서를 통해 알게 된 간단한 정보로는 익명클래스(인터페이스 포함)의 사용을 더욱 더 간결하게 해준다는 장점이 있었다.
JDK8버전부터는 람다 표현식과 스트림 기능을 추가하게 됐다. 즉 인터페이스를 익명클래스로 사용할 때 간단한 표현식으로 나타내는 것이 '람다 표현식' 인 것이다.
Oracle에서 제공한 예제를 통해 살펴보자.
package lambda;
import java.time.LocalDate;
import java.time.chrono.IsoChronology;
import java.util.ArrayList;
import java.util.List;
interface CheckPerson2 {
boolean test(Person p);
}
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
Person(String nameArg, LocalDate birthdayArg,
Sex genderArg, String emailArg) {
name = nameArg;
birthday = birthdayArg;
gender = genderArg;
emailAddress = emailArg;
}
interface CheckPerson {
boolean test(Person p);
}
public int getAge() {
return birthday
.until(IsoChronology.INSTANCE.dateNow())
.getYears();
}
public void printPerson() {
System.out.println(name + ", " + this.getAge());
}
public Sex getGender() {
return gender;
}
public String getName() {
return name;
}
public String getEmailAddress() {
return emailAddress;
}
public LocalDate getBirthday() {
return birthday;
}
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}
public static List<Person> createRoster() {
List<Person> roster = new ArrayList<>();
roster.add(
new Person(
"Fred",
IsoChronology.INSTANCE.date(1980, 6, 20),
Person.Sex.MALE,
"fred@example.com"));
roster.add(
new Person(
"Jane",
IsoChronology.INSTANCE.date(1990, 7, 15),
Person.Sex.FEMALE, "jane@example.com"));
roster.add(
new Person(
"George",
IsoChronology.INSTANCE.date(1991, 8, 13),
Person.Sex.MALE, "george@example.com"));
roster.add(
new Person(
"Bob",
IsoChronology.INSTANCE.date(2000, 9, 12),
Person.Sex.MALE, "bob@example.com"));
return roster;
}
public static void printPersons(
List<Person> roster, CheckPerson tester) {
System.out.println(tester);
System.out.println(tester instanceof CheckPerson);
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
public static void main(String[] args) {
List<Person> roster = Person.createRoster();
// 1. 비교하는 메서드를 가진 클래스를 하나 새로 생성하고 그 클래스의 객체를 매개변수로 넘겨줌.
printPersons(
roster, new CheckPersonEligibleForSelectiveService());
// 2. Person 클래스의 내부 인터페이스를 구현했을 때. 이는 인터페이스를 함수화 시켜줌.
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
// 3. 2번의 예제에서 람다 표현식을 썼을떄의 예제
printPersons(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
// 람다 표현식에서 body부분에 return값을 설정하는 예제
printPersons(
roster,
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
);
}
}
class CheckPersonEligibleForSelectiveService implements Person.CheckPerson {
public boolean test(Person p) {
return p.gender == Person.Sex.MALE &&
p.getAge() >= 18 &&
p.getAge() <= 25;
}
}
우선 전체코드이다. Person이라는 클래스에서는 여러개의 필드가 명시돼있다.
눈여겨봐야할 곳은 Person클래스 내부에 선언한 CheckPerson 인터페이스와 CheckPerson을 매개변수로 받는 printPerson메서드이다.
* 추가로 명시하자면 내부 인터페이스로 익명클래스로 부를 수 있다고 한다. 또한 실제로 컴파일러는 내부 클래스에 관한 처리도 익명클래스로 인식하고 동일하게 한다고 한다.
인터페이스를 구현할 때 가끔 인터페이스의 객체를 실수로 생성하게 되면 아래와 같이 생성된 경험이 있다.
CheckPerson checkPerson = new CheckPerson() {
@Override
public boolean test(Person p) {
return false;
}
};
이는 익명클래스와 인터페이스가 객체 생성이 불가능하기 때문에 익명클래스로서 동작하기 위해 컴파일러가 자동으로 생성해주는 것이다.
여기서 printPersons 메서드를 총 3번을 호출하게 되는데 이 때의 방법은 제 각각 다르다.
1. CheckPerson으로부터 구현받은 CheckPersonEligibleForSelectiveService클래스를 매개변수로 넘겨준다.
2. Person 클래스의 내부 인터페이스인 CheckPerson을 익명클래스로 생성한다.
3. 2번을 람다표현식으로 나타냈을 때
printPersons메서드 내부적으로 출력문을 찍어보면 다음과 같다.
lambda.CheckPersonEligibleForSelectiveService@49e4cb85
Bob, 23
lambda.Person$1@5ae9a829
Bob, 23
lambda.Person$$Lambda$20/0x00000008000b1040@548b7f67
Bob, 23
lambda.Person$$Lambda$21/0x00000008000b1440@6d78f375
Bob, 23
그렇다면 또 한가지 의문점은 PrintPersons의 메서드는 두번째 매개변수로 CheckPerson의 객체만 받아야 하는데 arrow(->)를 이용한 람다 표현식에는 객체에 대한 아무런 타입도 명시돼있지 않음에도 Person클래스라고 인식된다.
이는 내부적으로 구현된 인터페이스의 타입에 맞게 알아서 매핑되기 때문에 람다표현식을 통해 표현한 p는 곧 CheckPerson객체에 속하는 객체라고 인식하고 동작되는 듯하다.
'프로그래밍 > Java' 카테고리의 다른 글
[Java] 중첩클래스 (내부클래스, 멤버클래스) (0) | 2023.10.20 |
---|---|
[Java, JavaScript] 웹 소켓을 이용해 다중 1대1 채팅방 구현하기(DB 연동) (0) | 2023.09.20 |
[JSP] JSP란 무엇인가(feat.Servlet과 차이점?) (0) | 2023.06.14 |
[Java] equals 메서드를 오버라이딩 하는 이유 (0) | 2023.04.26 |
[Java] Collection( List, Set, Map )이란? (2) | 2023.04.21 |