모던 자바 문법

sookyoung.k·2024년 5월 30일
0

☕Java

목록 보기
2/11
post-thumbnail

자바 8버전을 기점으로 자바의 기능과 문법이 획기적으로 변화했다!

자바 8 버전의 주요 문법
→ 람다 표현식, 스트림 API, Optional

⚜️ 람다 표현식

익명 함수 - 함수는 함수이지만 이름이 없는 함수

"자바 기본 문법에서는 함수만 따로 선언할 수 없고 클래스가 있어야 한다."

🧐 클래스를 먼저 선언한 다음 그 안에 함수를 선언해야 그 전체를 메서드라고 부를 수 있다. 클래스와 메서드에 모두 이름을 붙여줘야 하나의 함수로 사용할 수 있었다!!

임시로 사용할 함수 때문에 매번 클래스 선언, 메서드 선언을 해야 하는 것은 상당히 불편한 일이다.

하지만 람다 표현식을 사용하면 코드가 무척이나 간결해진다.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList<String>();

        list.add("public");
        list.add("static");
        list.add("void");

        // 익명 클래스 코드
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String str1, String str2) {
                return str1.compareTo(str2);
            }
        });

        // 람다 표현식 코드
        list.sort((Comparator<String>) (str1, str2) -> str1.compareTo(str2));
    }
}

🦙 파라미터 1개, 명령문 1개

var1 -> System.out.println(var1)
  • 명령문의 결과가 람다 표현식의 리턴값이 된다
  • System.out.println은 리턴 값이 void이기 때문에 이 람다 표현식의 리턴 값도 void

🦙 파라미터 1개, 명령문 2개 이상

var1 -> {
	var1 = var1 + 1;
    System.out.println(var1);
    return var1;
}
  • 중괄호 생략 불가능
  • 명시적으로 var1을 return하고 있다고 가정하고 있지만 return문이 없는 경우 람다 표현식의 리턴 값은 void가 된다

🦙 파라미터 2개 이상, 명령문 1개

(var1, var2) -> System.out.println(var1 + var2)
  • 명령문의 결과가 그대로 람다 표현식의 리턴 값

🦙 파라미터 2개 이상, 명령문 2개 이상

(var1, var2) -> {
    System.out.println(var1);        
    System.out.println(var2);
}
  • 파라미터는 괄호로 감싸고, 함수 역시 중괄호로 감싸야 함

자바스크립트와 화살표 표기법만 다르군요

⚜️ 스트림 API

컬렉션에 추가된 메서드의 집합

  • 컬렉션에 담겨 있는 데이터를 마치 물이 흐르는 것처럼 처리한다는 데서 유래한 이름이다
  • 컬렉션에 담긴 데이터를 처리하는 존재
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList<String>();

        list.add("public");
        list.add("static");
        list.add("void");

        // for 문으로 List 순회
        for (int i = 0; i < list.size(); i++ {	// list.size()는 리스트의 크기 반환
        	System.out.println(list.get(i));	// i 번째 요소 출력 
        }
        
        // for 문을 스트림 API로 바꾸기 => forEach() 메서드 사용
        list.stream().forEach(str -> System.out.println(str));
    }
}

실행 결과 >

public
static
void

반복문을 위한 변수 i를 알 필요가 없다!

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Integer[] integerArray = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        List<Integer> list = Arrays.asList(integerArray);

		// java for문
        List evenList = new ArrayList<Integer>();

        for (int i = 0; i < list.size(); i++) {
            Integer number = list.get(i);
            if (number % 2 == 0) { // 2로 나눴을 때의 나머지가 0이면 2의 배수다.
                evenList.add(number);
            }
        }

        for (int i = 0; i < evenList.size(); i++) {
            System.out.println(evenList.get(i));
        }
        
        // java 스트림 API
        List evenList = list.stream()
                .filter(value -> value % 2 == 0).collect(Collectors.toList());

        evenList.stream().forEach(value -> System.out.println(value));
    }
}

filter() 메서드를 사용하여 코드를 훨씬 간결하게 만들었다.

🐳 forEach()

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Integer[] integerArray = new Integer[]{1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(integerArray);
        list.stream().forEach(value -> System.out.println(value));
    }
}

컬렉션의 요소들을 하나씩 꺼내어 반복하기 때문에 반복문을 쉽게 대체할 수 있다.

🐳 filter()

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        Integer[] integerArray = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        List<Integer> list = Arrays.asList(integerArray);
        List evenList = list.stream()
               .filter(value -> value % 2 == 0).collect(Collectors.toList());
        evenList.stream().forEach(value -> System.out.println(value));
    }
}

컬렉션의 요소들 중 조건문에 맞는 요소만 뽑아 새로운 스트림을 만든다.

🐳 distinct()

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Integer[] integerArray = new Integer[]{1, 1, 1, 1, 2, 2, 2, 3, 3, 4};
        List<Integer> list = Arrays.asList(integerArray);
        List<Integer> distinctList = list.stream().distinct().toList();
        distinctList.stream().forEach(value -> System.out.println(value));
    }
}

컬렉션의 요소에서 중복을 제거한다.

중복을 제거한다는 행위가 이미 정해져 있기 때문에 람다 표현식을 함수의 인자로 넣어줄 필요가 없고, 중복인지 아닌지를 판단하는 것은 요소들의 equals()메서드이다.

🐳 map()

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        String[] lowercaseArray = new String[]{"public", "static", "void"};
        List<String> lowercaseList = Arrays.asList(lowercaseArray);
        List<String> uppercaseList = lowercaseList.stream()
                .map(value -> value.toUpperCase()).toList();
        uppercaseList.stream().forEach(value -> System.out.println(value));
    }
}

컬렉션의 요소들에 특정 연산을 적용한 새로운 스트림을 만든다.

🐳 collect()/toList()

앞서 소개한 메서드들과 함께 .collect(Collectors.toList()) 형태로 사용하면 스트림을 간단하게 리스트로 만들 수 있다. 자바 16버전 부터는.toList() 사용해 더욱 간결하게 코드를 작성할 수 있다.

⚜️ Optional

NullPointException을 우아하게 해결하기 위해 등장!

public class Main {
    private static String getSomeString() {
        return null; // 이 메서드는 항상 null을 반환한다.
    }

    public static void main(String[] args) {
        String isThisNull = getSomeString();

        if(null != isThisNull) {
            System.out.println(isThisNull.toUpperCase());
        }
    }
}

이런 식으로 어떤 메서드를 호출했다면 해당 메서드에 null이 반환되었는지를 반드시 체크해야 한다. 체크하지 않으면 NullPointException이 발생!

이 때 Optional을 사용하면 if 문으로 null을 체크하는 코드를 다음과 같이 개선할 수 있다.

import java.util.Optional;

public class Main {
    private static Optional<String> getSomeString() {
        return Optional.empty(); // null을 반환하는 것이 아니라 비어있는 Optional을 반환한다.
    }

    public static void main(String[] args) {
        Optional<String> isThisNull = getSomeString();

        isThisNull.ifPresent(str -> System.out.println(str.toUpperCase()));
    }
}

Optional로 선언된 메서드에서 반환된 문자열이 비어 있지 않은 경우 ifPresent()의 인자로 들어간 람다 표현식을 실행하고, 비어있는 경우 실행되지 않는다.

값을 포함한 Optional을 반환하려면 아래와 같이 작성한다.

import java.util.Optional;

public class Main {
    private static Optional<String> getSomeString() {
        return Optional.ofNullable("public static void");
    }

    public static void main(String[] args) {
        Optional<String> isThisNull = getSomeString();

        isThisNull.ifPresent(str -> System.out.println(str.toUpperCase())); // PUBLIC STATIC VOID가 출력된다.
    }
}

🧐 안티패턴

디자인 패턴이란?
소프트웨어 개발 과정에서 자주 나타나는 문제 해결 방법 중 다른 분야에서도 재사용하기 좋은 코드의 패턴을 모아 이름을 붙인 것.

안티패턴은 이와 반대로 개발 과정에서 자주 나타나지만, 비효율적이거나 생산적이지 않은 패턴을 말한다.


<이것이 백엔드 개발이다 with 자바> 책을 공부하고 정리한 내용입니다.

profile
영차영차 😎

0개의 댓글