자바의 람다식은 왜 등장했을까?
람다식과 익명 클래스는 어떤 관계가 있을까?
람다식의 문법은 어떻게 될까?

자바 문서에 있는 예시를 그냥 코드 예시로 사용하겠다.
1. 간결성
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
HelloWorld 인터페이스가 있을 때
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
보통 인터페이스를 상속 받아 인터페이스의 멤버들을 재정의하고, 객체를 생성해서 사용한다. 익명 클래스를 사용하면?
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
객체 정의와 인스턴스 생성이 한 번에 이루어진다.
해당 클래스가 필요한 곳에서 직접 정의되어 사용되기 때문에 코드의 이해도와 가독성을 높일 수 있다.
2. 일회성
클래스가 한 번만 사용된다면, 익명 클래스는 특정 상황에서만 필요한 동작을 정의하고 사용하기 때문에 유지보수와 메모리에서 보다 효율적이다.
3. 이벤트 핸들링
GUI(그래픽 사용자 인터페이스) 애플리케이션에서 자주 사용된다.
1. 필드
클래스의 여러 메소드에서 사용될 때
class Spanish {
private HelloWorld spanishGreeting = new HelloWorld() {
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
System.out.println("Hola " + someone);
}
};
public void greetAll() {
spanishGreeting.greet();
}
public void greetFriend() {
spanishGreeting.greetSomeone("amigo~");
}
}
2. 메서드 내부
인터페이스 구현체가 특정 메서드에서만 사용될 때
public void korean() {
HelloWorld koreanGreeting = new HelloWorld() {
public void greet() {
greetSomeone("모두들");
}
public void greetSomeone(String someone) {
System.out.println("안녕 " + someone);
}
};
koreanGreeting.greet();
}
3. 파라미터
class Jpan {
public void greetAll(HelloWorld japanishGreeting) {
japanishGreeting.greet();
}
}
public static void main(String[] args) {
Jpan jpan = new Jpan();
jpan.greetAll(new HelloWorld() {
public void greet() {
greetSomeone("みんな");
}
public void greetSomeone(String someone) {
System.out.println("こんにちは " + someone);
}
});
}
참고.
익명 클래스에서는 생성자를 선언할 수 없다. 이름이 없으니까!
정리.
익명 클래스는 부모 클래스의 자원을 일회성으로 이용하고 싶을 때 사용한다.
1. 재사용성의 한계
익명 클래스를 다시 사용할 필요가 있는 경우, 같은 구현을 반복해서 작성해야 한다. 코드의 중복이 발생한다.
2. 가독성 저하
익명 클래스 장점에 높은 가독성이 있긴 하지만 반대로 가독성이 저하될 수 있다. (ex.익명 클래스가 긴 코드 블록을 가지고 있을 때) 따라서 코드의 가독성과 유지보수성을 저하시킬 수 있으므로 상황에 맞게 사용해야 한다.
3. 유연성의 한계
익명 클래스는 하나의 클래스나 인터페이스만 구현할 수 있다. 인터페이스의 매력이 사라진다..
위에서는 인터페이스를 상속받은 클래스 예제들만 보았는데 부모 클래스를 상속받은 익명 객체도 있다
인터페이스 익명 객체와 익명 클래스의 차이
1. 구현 방식
- 익명 객체: 익명 객체는 인터페이스의 구현체. 인터페이스의 메서드를 오버라이드하는 것.
- 익명 클래스: 익명 클래스는 부모 클래스를 상속받은 이름이 없는 클래스.
2. 유연성
- 익명 객체: 해당 인터페이스에 정의된 메서드만 사용 가능.
- 익명 클래스: 익명 클래스는 부모 클래스의 모든 멤버에 접근 가능. 새로운 멤버 추가 가능.
- 주의! 새로 정의한 멤버는 외부에서 사용 불가3. 재사용성
- 익명 객체: 인터페이스의 구현체를 한 번만 만들어 사용하는 경우에 적합.
- 익명 클래스: 익명 클래스 여러 번 사용 가능.

람다 표현식을 사용하면 기능을 메서드 인수로 처리하거나 코드를 데이터로 처리할 수 있다. ??
람다는 자바 8부터 도입된 함수적 인터페이스 익명 객체를 구현하는데 사용하는 표현식이다. 즉, 람다식은 함수적 인터페이스에만 사용할 수 있다. 자바의 람다식은 코드를 간결하게 작성하고, 함수형 프로그래밍 스타일을 지원하기 위해 등장했다.
함수적 인터페이스란?
단 하나의 추상 메서드만 선언된 인터페이스.
람다식을 다루기 위한 인터페이스.
- 람다식은 모든 인터페이스에서 사용한다(x)
→ 람다식은 하나의 메소드를 정의하기 때문에 하나의 추상 메소드만 선언된 인터페이스만 타겟이 된다.- 람다식은 함수적 인터페이스에서 사용한다(o)
@FunctionalInterface
컴파일러는 메소드가 몇개 선언되어있는지 확인하 메소드가 두개이상 선언되어있다면 에러가 발생한다.
생략 할 수 있으며 함수적 인터페이스라는것을 명확하게 하기 위해 사용된다.!
(매개변수, ...) -> {실행문...}
인터페이스와 클래스, 사용할 메소드
interface CheckPerson {
boolean test(Person p);
}
class CheckPersonEligibleForSelectiveService implements CheckPerson {
public boolean test(Person p) {
return p.gender == Person.Sex.MALE &&
p.getAge() >= 18 &&
p.getAge() <= 25;
}
}
public static void printPersons(List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
CheckPerson 인스턴스를 만들고 test 메서드를 재정의 한다. 클래스를 만들 필요가 없기 때문에 필요한 코드 양은 줄어들었다. 하지만 CheckPerson 인터페이스에는 메서드가 하나만 있고, 이 경우 익명 클래스 대신 람다 식을 사용하면 더 간결하게 만들 수 있다!
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
printPersons(
roster,
(Person p) -> { p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
};
);
printPersons(
roster,
(p) -> { p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
};
);
printPersons(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
// 다른 예시
btn.setOnAction(
event -> System.out.println("Hello World!")
);
코드가 굉장히 간결해졌다.
1. 가독성 감소
람다식이 복잡할 경우 코드의 의도를 파악하기 어려울 수 있다.
2. 디버깅의 어려움
람다식은 익명으로 정의되기 때문에 디버깅이 어려울 수 있다.
3. 성능
람다식을 사용할 경우 해당 코드가 실행될 때마다 인스턴스가 생성하고 초기화되기 대문에 이에 따른 오버헤드가 발생할 수 있다.
참고
https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#syntax
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
https://inpa.tistory.com/entry/%E2%98%95-Lambda-Expression