본 포스팅은 프로그래머스 자바 중급 강의에 기반하여 작성되었습니다.
자바 강의의 모든 내용이 적힌것이 아닌 복습시 놓쳤던 부분을 하나하나 정리한 내용입니다.
JAVA8에서 새롭게 추가된 문법 람다식
람다식
람다식은 다른말로 익명 메소드라고도 한다.
- 인터페이스 중에서 메소드를 하나만 가지고 있는 인터페이스를 함수형 인터페이스라고 한다.
- 예를들어 쓰레드를 만들때 사용하는 Runnable 인터페이스의 경우 run()메소드를 하나만 가지고 있다.
- Runnable을 이용하여 쓰레드를 만드는 방법
public class LambdaExam1 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run(){
for(int i = 0; i < 10; i++){
System.out.println("hello");
}
}
}).start();
}
}
- 쓰레드가 실행되면 쓰레드 생성자 안에 넣은 run()메소드가 실행된다.
- 자바는 메소드만 매개변수로 전달할 방법이 없다. 인스턴스만 전달 할 수 있다.
- 그렇기때문에 run()메소드를 가지고 있는 Runnable객체를 만들어서 전달한다.
메소드만 전달할 수 있다면, 좀더 편리하게 프로그래밍할 수 있을텐데,
자바는 메소드만 전달할 수 있는 방법은 없었기 때문에
매번 객체를 생성해서 매개변수로 전달해야 했다.
이런 부분을 해결한 것이 람다표현식이다.
- 람다식을 이용해서 수정한 코드
public class LambdaExam1 {
public static void main(String[] args) {
new Thread(()->{
for(int i = 0; i < 10; i++){
System.out.println("hello");
}
}).start();
}
}
- ()->{ ..... } 부분이 람다식, 다른말로 익명 메소드
- JVM은 Thread생성자를 보고 ()->{} 이 무엇인지 대상을 추론한다.
- Thread생성자 api를 보면 Runnable인터페이스를 받아들이는 것을 알 수 있다.
- JVM은 Thread생성자가 Runnable인터페이스를 구현한 것이 와야 하는 것을 알게 되고 람다식을 Runnable을 구현하는 객체로 자동으로 만들어서 매개변수로 넣어준다.
람다식 기본문법
(매개변수목록) -> {실행문}
- 2개의 값을 비교하여 어떤 값이 더 큰지 구하는 compareTo()라는 메소드를 갖고 있는 Compare 인터페이스 정의
- 2개의 값을 받아들인 후, 정수를 반환하는 메소드를 선언
public interface Compare{
public int compareTo(int value1, int value2);
}
- Compare 인터페이스를 이용하는 클래스
- Compare 인터페이스를 받아들인 후, 해당 인터페이스를 이용하는 exec메소드
- compareTo메소드가 어떻게 구현되어 있느냐에 따라서 출력되는 값이 다름
public class CompareExam {
public static void exec(Compare compare){
int k = 10;
int m = 20;
int value = compare.compareTo(k, m);
System.out.println(value);
}
public static void main(String[] args) {
exec((i, j)->{
return i - j;
}); }
}
실행결과
-10
JVM은 exec 메소드를 찾아보고 매개변수 두개를 받아들이는 인터페이스를 받아들이고 있는 메소드가 무엇인지
찾아본다. 해당 인터페이스가 compare라는 인터페이스를 받아들이는 exec메소드가 알맞는 메소드인지 찾게된다.
JVM은 람다식을 compare을 구현하는 익명객체로 만들어서 exec에게 전달한다.
사용자가 어떻게 메소드를 구현했는지에 따라서 결과값이 달라진다.
자바는 메소드만 인자로 전달하려면 반드시 객체를 만들어서 전달해야 했다.
Java8에 람다식이 생기면서, 마치 함수만 전달하는 것처럼 간편하게 문법을 사용할 수 있게 되다.
예제로 알아보는 람다, 내부클래스, 익명클래스
내부클래스, 익명클래스, 람다를 왜 사용하는지 자바를 처음 시작할 때는 이해하기 어려울 수 있습니다.
지금은 예제를 보면서 "저렇게도 쓸 수 있구나!" 정도로 이해하면 됩니다.
내부클래스, 익명클래스, 람다를 이용해서 같은 작업을 어떻게 다르게 할 수 있는지
살펴보면서 각각이 어떻게 쓰이는지 눈여겨보세요.
다음 코드는 앞으로 예제에서 사용할 Car클래스입니다.
public class Car{
//이어지는 예제에서 사용할 Car클래스입니다.
//이름, 탑승인원, 가격, 사용년수를 필드로 가집니다.
public String name;
public int capacity;
public int price;
public int age;
//각각의 필드를 생성자에서 받아서 초기화합니다.
public Car(String name, int capacity, int price, int age){
this.name = name;
this.capacity = capacity;
this.price = price;
this.age = age;
}
//Car 객체를 문자열로 출력하면 이름을 출력합니다.
public String toString(){
return name;
}
public static void main(String args[])
{
Car car = new Car("new model", 4, 3000, 0);
}
}
main에서는 다양한 조건의 Car객체를 만들어서 cars라는 리스트에 넣습니다.
이 cars라는 리스트에 있는 차를 검색해서 조건에 맞는 차를 출력하는 예제들을 살펴볼 텐데요.
첫 번째로 가격이 2000보다 싼 차량을 검색해서 이름을 출력하는 printCarCheaperThan이라는 함수가 있습니다.
import java.util.*;
public class CarExam{
public static void main(String[] args){
//Car객체를 만들어서 cars에 넣습니다.
List<Car> cars = new ArrayList<>();
cars.add( new Car("작은차",2,800,3) );
cars.add( new Car("봉고차",12,1500,8) );
cars.add( new Car("중간차",5,2200,0) );
cars.add( new Car("비싼차",5,3500,1) );
printCarCheaperThan(cars, 2000);
}
public static void printCarCheaperThan(List<Car> cars, int price){
for(Car car : cars){ // for-each구문 배열 하나씩 집어넣음
if(car.price < price){
System.out.println(car);
}
}
}
}
실행결과
작은차
봉고차
이번에는 조건이 더 복잡한 경우입니다. 내부클래스를 이용해서 CheckCar라는 인터페이스를 만들고, 그걸 구현하는 CheckCarForBigAndNotExpensive클래스를 만들어서 4명 이상이 탈 수 있고, 가격이 2500이하인 차를 검색합니다.
import java.util.*;
public class CarExam{
public static void main(String[] args){
List<Car> cars = new ArrayList<>();
cars.add( new Car("작은차",2,800,3) );
cars.add( new Car("봉고차",12,1500,8) );
cars.add( new Car("중간차",5,2200,0) );
cars.add( new Car("비싼차",5,3500,1) );
printCar(cars, new CheckCarForBigAndNotExpensive());
}
public static void printCar(List<Car> cars, CheckCar tester){
for(Car car : cars){
if (tester.test(car)) {
System.out.println(car);
}
}
}
// 인터페이스
interface CheckCar{
boolean test(Car car);
}
//내부클래스를 만들어서 사용합니다.
static class CheckCarForBigAndNotExpensive implements CheckCar{
public boolean test(Car car){
return car.capacity >= 4 && car.price < 2500;
}
}
}
실행결과
봉고차
중간차
같은 검색조건에 대해 익명 클래스를 이용하면 별도 클래스를 만들 필요가 없으므로 코드가 조금 더 짧아집니다.
import java.util.*;
public class CarExam{
public static void main(String[] args){
List<Car> cars = new ArrayList<>();
cars.add( new Car("작은차",2,800,3) );
cars.add( new Car("봉고차",12,1500,8) );
cars.add( new Car("중간차",5,2200,0) );
cars.add( new Car("비싼차",5,3500,1) );
printCar(cars,
//인터페이스 CheckCar를 구현하는 익명클래스를 만듭니다.
new CheckCar(){
public boolean test(Car car){
return car.capacity >= 4 && car.price < 2500;
}
});
}
public static void printCar(List<Car> cars, CheckCar tester){
for(Car car : cars){
if (tester.test(car)) {
System.out.println(car);
}
}
}
interface CheckCar{
boolean test(Car car);
}
}
실행결과
봉고차중간차
같은 검색조건에 대해 람다를 이용하면 메소드 지정도 필요 없으므로 더 간결하게 표현할 수 있습니다.
import java.util.*;
public class CarExam{
public static void main(String[] args){
//Car객체를 만들어서 cars에 넣습니다.
List<Car> cars = new ArrayList<>();
cars.add( new Car("작은차",2,800,3) );
cars.add( new Car("봉고차",12,1500,8) );
cars.add( new Car("중간차",5,2200,0) );
cars.add( new Car("비싼차",5,3500,1) );
CarExam carExam = new CarExam();
carExam.printCar(cars,
//인터페이스 CheckCar의 test메소드에 대응하는 람다를 만듭니다.
(Car car) -> { return car.capacity >= 4 && car.price < 2500; }
);
}
public void printCar(List<Car> cars, CheckCar tester){
for(Car car : cars){
if (tester.test(car)) {
System.out.println(car);
}
}
}
interface CheckCar{
boolean test(Car car);
}
}
'프로그래밍 공부 > JAVA' 카테고리의 다른 글
빌드와 실행, Ant, Maven, Gradle 이란 무엇인가? (0) | 2021.09.03 |
---|---|
JDK, JRE, JVM은 무엇인가? (0) | 2021.09.03 |
JAVA중급강의 정리 어노테이션, 쓰레드 (0) | 2021.08.27 |
JAVA 중급강의 정리 자바IO 입력과 출력 (0) | 2021.08.18 |
JAVA 중급강의 정리 Date, Calendar, java.time패키지 (0) | 2021.08.11 |