반응형
반응형
public class SwitchExample {
    public static void main(String[] args) {
        int dayOfWeek = 3;

        String dayType = switch (dayOfWeek) {
            case 1, 2, 3, 4, 5 -> "Weekday";
            case 6, 7 -> "Weekend";
            default -> throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeek);
        };

        System.out.println("Day type: " + dayType);
    }
}

Java17부터 →을 이용해 각각 break를 안 사용해도 되고 가독성을 높여주는 Syntactic Sugar이다

반응형
반응형

 

📝@Rollback

@Transactional
@Rollback(value = false)
class MemberRepositoryTest {
    ...
}

@Transactional에서 장애 발생시 롤백 시키는 걸 할지 말지에 대한 것이다. 일반적으로 사용하지 않으며 테스트 코드 작성해서 디비 데이터 값을 확인해야한다면 사용한다 (테스트 코드 작동한 후 종료된 뒤에 바로 롤백 시켜서 디비에서 확인하기가 어려움)

 

📝@PostConstruct

// 초기 데이터 INSERT
@PostConstruct
public void insertData() throws Exception {

    IntStream.range(0, 100).forEach((i) -> {
        Member member = new Member("username" + i);
        memberRepository.save(member);
    });
}

종속성 주입이 완료된 후 실행되어야 하는 메서드에 사용한다 여기에서는 정상으로 스프링부트가 기동 된다면 해당 메소드가 동작해 초기 데이터를 INSERT하는 작업이다.

 

📝@CrossOrign

@CrossOrigin(origins = "*")
@GetMapping("/next")
public @ResponseBody HashMap<String, Object> next() throws InterruptedException {

	System.out.println("next2 call!!");
	HashMap<String, Object> map = new HashMap<String, Object>();
	map.put("id", "Next 테스트");
	
	Thread.sleep(3000);
	
	return map;
}

어떤 라우팅에 대해서 URL 요청을 허용할 지

반응형
반응형

📝@EnableJpaAuditing, @EntityListeneres, @CreateBy, @LastModifiedBy, @CreateDate, @LastModifiedDate

@EnableJpaAuditing // 필수
@SpringBootApplication
public class DataJpaApplication {

	public static void main(String[] args) {
		SpringApplication.run(DataJpaApplication.class, args);
	}

	@Bean
	public AuditorAware<String> auditorProvider() {
		return () -> Optional.of(UUID.randomUUID().toString());
		// 실무에서는 세션 정보나, 스프링 시큐리티 로그인 정보에서 ID를 받음
	}

}

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;

}

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseTimeEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

}


@Entity
public class Member extends BaseEntity {

    @Id
    @GeneratedValue
    private String id;
    
    private String username;

}

 

  • @EnableJpaAuditing
    • 아래 어노테이션을 쓰기 위해 필수 기능이다
  • @EntityListeneres
    • 해당 엔터티는 JPA Entity에 이벤트 발생시 콜백 처리하라는 의미이다.
  • @CreateBy
    • 생성자
  • @LastModifiedBy
    • 수정자
  • @CreateDate
    • 등록일자
  • @LastModifiedDate
    • 수정일자

 

📝@PersistenceContext

@PersistenceContext
EntityManager em;

스프링 부트에서 생성한 EntityManager를 DI시켜준다

반응형
반응형

📝@NamedQuery, @Query

@Entity
@NamedQuery(
    name="Member.findByUsername", // Key
    query="select m from Member m where m.username = :username" // Value
)
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String username;
    private int age;

}

public interface MemberRepository extends JpaRepository<Member, Long> {

    /** ----- 방법1 (@Query) ----- **/
    @Query(name ="Member.findByUsername")
    List<Member> findByUsername(@Param("username") String username);
    
    /** ----- 기본 예제 ----- **/
    @Query("select m from Member m where m.username = :username and m.age = :age")
    List<Member> findUser(@Param("username") String username, @Param("age") int age);

}

조건절 조회하는 임의의 쿼리가 필요할 때 Entity에 @NamedQuery를 이용해 JpaRepository에서 {키:값} 형태로 찾아 사용이 가능하다

이 방법보단 JpaRepository에서 바로 @Query를 이용해 바로 작성하는 걸 더 많이 사용한다

 

📝@Modifying

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Modifying(clearAutomatically = true)
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);
 
}

Update한 후에 Update 항목을 조회하는 경우가 있을 때 영속성 컨텍스트 캐시를 날리고 데이터 조회하기 때문에 디비에서 조회하게 되고  데이터의 정합성을 맞춘다

 

📝@EntityGraph

public interface MemberRepository extends JpaRepository<Member, Long> {

    @EntityGraph(attributePaths = {"team"})
    @Query("select m from Member m")
    // 위에 코드는 EntityGraph + Query로 아래와 같은 코드이다. (team Fetch Join)

    // @Query("select m from Member m left join fetch m.team")
    List<Member> findMemberEntityGraph();
}

패치조인을 더 쉽게할 수 있도록 스프링 데이터 JPA에서 제공해준다.

 

📝엔터티 라이프 사이클 컨트롤 어노테이션

@PrePersist : 새로운 엔티티에 대해 persist가 호출되기 전
@PreUpdate : 엔티티 업데이트 작업 전
@PreRemove : 엔티티가 제거되기 전
@PostPersist : 새로운 엔티티에 대해 persist가 호출된 후
@PostUpdate : 엔티티가 업데이트된 후
@PostRemove : 엔티티가 삭제된 후
@PostLoad : Select조회가 일어난 직후에 실행

 

 

 

반응형
반응형

📝@Entity, @Id, @GeneratedValue, @Temporal, @Lob

public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // IDENTITY 전략
    // @GeneratedValue(strategy = GenerationType.SEQUENCE) // SEQUENCE 전략
    @Column(name="member_id")
    private Long id;

    private String name;
    private int age;
    private String address; // 필드만 추가해주면 끝

    // @Enumerated(EnumType.ORDINAL) // enum 타입 매핑 [숫자] (금지)
    @Enumerated(EnumType.STRING) // enum 타입 매핑 [문자] (사용 OK)
    private CategoryEnum name;

    // 날짜 타입 매핑 [아래 LocalDateTime을 쓰면 알아서 YYYYMMDDHHMMSS 설정]
    @Temporal(TemporalType.DATE) 
    private Date date;
    
    private LocalDateTime createDate
    
    @Lob // BLOB, CLOB 매핑
    private String description;
}

 

  • @Entity 
    • JPA에서 영속성 컨텍스트가 관리할 테이블이라는 의미이다.
  • @Id
    • 해당 필드를 PK로 지정하겠다는 의미이다.
  • @GeneratedValue
    • PK AutoIncrement설정이다. IDENTITY로 설정할지 SEQUENCE로 지정할지 정할 수 있다.
  • @Temporal
    • 날짜 필드라는 걸 알리는 의미이다.
  • @Lob
    • BLOB, CLOB타입이라는 걸 알리는 의미이다.

 

📝@JoinColumn, @ManyToOne, @OneToMany, @OneToOne, @ManyToMany

@Entity
public class Member {

    @Id // PK로 설정할 컬럼
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 10)
    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID") // Team 테이블[클래스]에 연결할 필드명을 알려주세요 
                                  // (JoinColumn은 FK가 있는 테이블에 들어간다)
    private Team team;

}

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;

}
  • @JoinColumn
    • 연결할 테이블에 필드를 지정한다
  • @ManyToOne
    • 다대일 관계로 연결하겠다는 의미
  • @OneToMany
    • 일대다 관계로 연결하겠다는 의미
  • @OneToOne
    • 일대일 관계로 연결하겠다는 의미
  • @ManyToMany (사용 X)
    • 다대다 관계로 연결하겠다는 의미 (다대다 관계는 무조건 다대일로 풀어내야한다)

 

 

📝@Inheritance, @DiscriminatorColumn, @DiscriminatorValue

@Inheritance(strategy = InheritanceType.JOINED) // 조인 전략
@DiscriminatorColumn 
@Entity
public class Item {

    @Id
    @GeneratedValue
    private long id;

    private String name;
    private int price;
}

@Entity
// @DiscriminatorValue("this is movie")
public class Movie extends Item{

    private String director;
    private String actor;

}

public class Main {
    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try{

            Movie movie = new Movie();
            movie.setName("바람과 함께 사라지다");
            movie.setPrice(10000);
            movie.setActor("이민호");
            movie.setDirector("봉준호");

            em.persist(movie);

            tx.commit();

        }catch (Exception e){
            e.printStackTrace();
        }finally{
            emf.close();
            em.close();
        }
    }
}

 

@Inheritance

관계형 데이터베이스에 상속관계라는 걸 JPA에서 표현했다

 

 

 

 

@DiscriminatorColumn

해당 어노테이션을 사용 안 하면 DTYPE이 따로 안 보이는데 이걸 명시적으로 표현해주는 어노테이션 어느 자식이 UPSERT 되어서 그런지 잘 모르니 해당 어노테이션을 사용하는게 좋다

 

 

 

 

@DiscriminatorValue

해당 어노테이션을 사용하면 DTYPE에 들어갈 걸 명시적으로 지정할 수 있다.

 

 

 

 

 

 

 

 

📝@MappedSuperclass

@MappedSuperclass
public abstract class BaseEntity {

    @Column(name = "create_date") // 부모 클래스에서 컨트롤 가능
    private LocalDateTime localDateTime;

    private LocalDateTime modi_date;
}

@Entity
public class Book extends BaseEntity{

    @Id
    @GeneratedValue
    private long id;

    private String name;
    private String author;
    private String ISBN;

}

Inheritance와 차이가 있는 점은 Inheritance은 부모 자식 테이블 따로따로 들어가게 되는데 MappedSuperclass의 경우 필드만 공유하고 하나의 테이블로 만들어진다.

 

 

📝@NamedQuery, @Query

@Entity
@NamedQuery(
    name="Member.findByUsername", // Key
    query="select m from Member m where m.username = :username" // Value
)
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String username;
    private int age;

}

public interface MemberRepository extends JpaRepository<Member, Long> {

    /** ----- 방법1 (@Query) ----- **/
    @Query(name ="Member.findByUsername")
    List<Member> findByUsername(@Param("username") String username);
    
    /** ----- 기본 예제 ----- **/
    @Query("select m from Member m where m.username = :username and m.age = :age")
    List<Member> findUser(@Param("username") String username, @Param("age") int age);

}

조건절 조회하는 임의의 쿼리가 필요할 때 Entity에 @NamedQuery를 이용해 JpaRepository에서 {키:값} 형태로 찾아 사용이 가능하다

이 방법보단 JpaRepository에서 바로 @Query를 이용해 바로 작성하는 걸 더 많이 사용한다

 

📝@Modifying

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Modifying(clearAutomatically = true)
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);
 
}

Update한 후에 Update 항목을 조회하는 경우가 있을 때 영속성 컨텍스트 캐시를 날리고 데이터 조회하기 때문에 디비에서 조회하게 되고  데이터의 정합성을 맞춘다

 

📝@EntityGraph

public interface MemberRepository extends JpaRepository<Member, Long> {

    @EntityGraph(attributePaths = {"team"})
    @Query("select m from Member m")
    // 위에 코드는 EntityGraph + Query로 아래와 같은 코드이다. (team Fetch Join)

    // @Query("select m from Member m left join fetch m.team")
    List<Member> findMemberEntityGraph();
}

패치조인을 더 쉽게할 수 있도록 스프링 데이터 JPA에서 제공해준다.

 

엔터티 라이프 사이클 컨트롤 어노테이션 (@PrePersist, @PreUpdate, @PreRemove, @PostPersist, @PostUpdate, @PostRemove, @PostLoad)

@PrePersist : 새로운 엔티티에 대해 persist가 호출되기 전
@PreUpdate : 엔티티 업데이트 작업 전
@PreRemove : 엔티티가 제거되기 전
@PostPersist : 새로운 엔티티에 대해 persist가 호출된 후
@PostUpdate : 엔티티가 업데이트된 후
@PostRemove : 엔티티가 삭제된 후
@PostLoad : Select조회가 일어난 직후에 실행
반응형
반응형

📝메소드 레퍼런스

메소드 레퍼런스란 클래스에 포함된 메소드를 생략해 표현이 가능하다 이 점을 이용해 간략할 수도 있고 무엇보다 일급객체의 특징인 파라미터에 함수를 넣을 수 있다. (물론 적절한 함수형 인터페이스의 시그니처[함수형 인터페이스에 맞는 포맷]이면 메소드 레퍼런스를 안 쓰고도 가능하다)

 

  • System.out::println
Arrays.asList(1, 2, 3, 4, 5)
      .forEach(System.out::println);

forEach안에 System.out::println [클래스명:메소드명]을 넣을 수 있다.

함수형 인터페이스를 이용하면 파라미터 및 반환타입 추론이 가능하기 때문에 생략할 수 있다.

 

  • 나만의 클래스:메소드를 축약해서 사용해보기

public class MethodLambdaTest {

    @Test
    public void testing(){

        System.out.println(
            Arrays.asList(new BigDecimal("10.0"), new BigDecimal("23"), new BigDecimal("5"))
                  .stream()
                   // .sorted()                                 // ✅ 동일
                   // .sorted((bd1, bd2) -> bd1.compareTo(bd2)) // ✅ 동일
                   // .sorted(BigDecimalUtil::compare)          // ✅ 동일
                  .sorted(BigDecimal::compareTo)                // ✅ 동일
                  .collect(toList())
        ); // [5, 10.0, 23]
    }
}

class BigDecimalUtil {
    public static int compare(BigDecimal bd1, BigDecimal bd2){
        return bd1.compareTo(bd2);
    }
}

 

  • sorted 함수 설명
Stream<T> sorted(Comparator<? super T> comparator) 
...

@FunctionalInterface
public interface Comparator<T> {
   int compare(T o1, T o2)
}

 

sorted는 Comparator를 받고 Comparator의 경우 (T, T) -> int이기 때문에 생략해도 알아서 비교값들이 들어간다

 

 

 

  • anyMatch에 equals 함수 적용시키기
@Test
public void testing(){
	final String targetString = "c";
    System.out.println(
        Arrays.asList("a", "b", "c", "d")
                .stream()
                //.anyMatch(x -> x.equals("c")) // ✅ 동일
                //.anyMatch(String::equals)     // ❌ 동작 X
                .anyMatch(targetString::equals) // ✅ 동일
                //.anyMatch("c"::equals)        // ✅ 동일
    );
}

 

  • anyMatch에 String::equals 동작 안 하는 이유
boolean anyMatch(Predicate<? super T> predicate);

...

@FunctionalInterface
public interface Comparator<T> {
   int compare(T o1, T o2)
}

anyMatch의 경우 Predicate 받고 Predicate의 경우 T -> boolean이기 때문에 String::equals의 반환 타입과 맞지만
"c".eqauls("d")와 같이 두개의 비교값 즉, 두개 파라미터가 필요하기 때문에 시그니처가 맞지 않아서 동작하지 않는다.

반응형
반응형

📝Stream

Java8에서 나온 것으로 매개변수들이 함수형 인터페이스 기반으로 만들어져 Stream에서 쓰는 내부 함수들은 Predicate, Function처럼 함수형 인터페이스를 사용한다

// Stream 인터페이스 내부 함수
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

Stream 장점으로는 Lazy Evaluation으로 이 의미는 즉, 불필요한 연산을 피한다는 의미이다.

배열의 값을 하나씩 읽어서 순차적으로 진행시킨다.  밑에 코드에서 보는 것 처럼 i < 6이라는 조건절이 있는 경우 1 ~ 10까지 i < 6처리한 후에 남은 애들이 2번째  조건 절인 i % 2 == 0을 거치는게 아니라 "1"이 i < 6을 거친 후 i % 2 == 0을 거치는 순차적인 Iterator로 돌아가게 된다.

 

Stream 동작 트래킹

@Test
public void test(){
    final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    System.out.println(
        list.stream()
            .filter(i -> {
                System.out.println(i + " : i < 6");
                return i<6;
            })
            .filter(i -> {
                System.out.println(i + " : i % 2 == 0");
                return i%2==0;
            })
            .collect(Collectors.toList())
    );
    /**
     * 1 : i < 6
     * 1 : i % 2 == 0
     * 2 : i < 6
     * 2 : i % 2 == 0
     * 3 : i < 6
     * 3 : i % 2 == 0
     * 4 : i < 6
     * 4 : i % 2 == 0
     * 5 : i < 6
     * 5 : i % 2 == 0
     * 6 : i < 6
     * 7 : i < 6
     * 8 : i < 6
     * 9 : i < 6
     * 10 : i < 6
     * [2, 4]
     */
}

 

여기서 예제를 보면 1 ~ 10을 1번 필터를 다 거친 후 남은 걸 2번 필터를 거치거나 각 Iterator들을 개별로 1번 → 2번을 거치거나 별 차이는 없어보인다 하지만 밑에 같은 예제의 경우 이야기가 달라진다. findFirst로 1개의 정상 값만 나오면 바로 종료시켜버린다 물론 IF로직같은 걸로 1개 일 때 종료하던가 로직 처리가 가능하지만 가독성 및 효율성이 떨어진다.

 

Stream 동작 트래킹

@Test
public void test(){
    final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    System.out.println(
        list.stream()
            .filter(i -> {
                System.out.println(i + " : i < 6");
                return i<6;
            })
            .filter(i -> {
                System.out.println(i + " : i % 2 == 0");
                return i%2==0;
            })
            .findFirst()
    );
    /**
     * 1 : i < 6
     * 1 : i % 2 == 0
     * 2 : i < 6
     * 2 : i % 2 == 0
     */
}

 

동작 방식을 이제 알아봤으니 이걸 이용해 익숙해져보자

 

Stream 사용해보기

package hello.exception;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

public class StreamTest {

    @Test
    public void intStreamTest(){

        /** ---- 연속적인 값 할당 ---- **/
        IntStream.range(0, 10).forEach(i -> System.out.println("range : " + i)); // range : 0 range : 1 .... range : 9
        // IntStream.iterate(1, i -> i + 1).forEach(i -> System.out.println("iterate : " + i)); // iterate : 1 ... iterate : 21
        IntStream.rangeClosed(0, 10).forEach(i -> System.out.println("rangeClose : " + i)); // rangeClose : 0 rangeClose : 1 ... rangeClose : 10

        /** ---- Sum ---- **/
        long sum = IntStream.of(1, 2, 3, 4, 5)
                .sum();

        System.out.println("sum : " + sum); // sum : 15

    }

    @Test
    public void streamTest(){

        /** ---- of, forEach ---- **/
        Stream.of(1, 2, 3, 4, 5)
                .forEach(i -> System.out.println("stream of : " + 1)); // stream of : 1 ... stream of : 5

    }

    @Test
    public void streamChaningTest(){

        /** ---- stream chaning ---- **/
        final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Optional<Integer> result =
                numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                        .filter(number -> number > 3) // 4, 5, 6, 7, 8, 9, 10
                        .filter(number -> number < 9) // 4, 5, 6, 7, 8
                        .map(number -> number * 2)    // 8, 10, 12, 14, 16
                        .filter(number -> number < 15) // 8, 10, 12, 14
                        .findAny(); // 8

        System.out.println("stream result : " + result.get());


        // Intermediate Operation Method [Stream 리턴 -> chaning 가능]
        // Terminated Operation Method   [Stream 외 리턴]
    }
    @Test
    public void streamChaningTest2(){

        final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        /** ---- collection(toList()) ---- **/
        List<String> result = numbers.stream()
                .filter(number -> number > 8) // 9, 10
                .map(number -> "#" + number)
                .collect(toList());

        System.out.println("result : " + result); // result : [#9, #10]

        /** ---- collection(joining) ---- **/
        String result2 = numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                .filter(number -> number > 8) // 4, 5, 6, 7, 8, 9, 10
                .map(number -> "#" + number)
                .collect(joining(",","[","]"));

        System.out.println("result2 : " + result2); // result2 : [#9,#10]

        /** ---- collection(toSet()) ---- **/
        Set<String> result3 = numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                .filter(number -> number > 8) // 4, 5, 6, 7, 8, 9, 10
                .map(number -> "#" + number)
                .collect(toSet());

        System.out.println("result3 : " + result3); // result3 : [#10, #9]

        /** ---- distinct ---- **/
        List<String> result4 =
                Stream.of(1, 3, 3, 5, 5)
                        .filter(number -> number > 2)
                        .map(number -> number * 2)
                        .map (number -> "#" + number) // [#6, #6, #10, #10]
                        .distinct()                   // [#6, #10]
                        .collect(toList());

        System.out.println("result4 : " + result4); // result4 : [#6, #10]

        /** —— count —— **/
        long result5 =
                Stream.of(1, 3, 3, 5, 5)
                        .filter(number -> number > 2)
                        .map(number -> number * 2)
                        .map (number -> "#" + number) // [#6, #6, #10, #10]
                        .distinct()                   // [#6, #10]
                        .count();

        System.out.println("result5 : " + result5); // result5 : 2

    }
    
	@Test
    public void reduceTest(){
        /** —— reduce —— **/
        List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer result =
                numbers.stream()
                        .reduce(100, (x, y) -> {
                            System.out.println("x : " + x + ", y :" + y);
                            return x+y;
                        });

        // reduce : 반복된 패턴을 추상화 할 수 있다.
        System.out.println("result : " + result);

        /**
         * x : 100, y :1
         * x : 101, y :2
         * x : 103, y :3
         * x : 106, y :4
         * x : 110, y :5
         * x : 115, y :6
         * x : 121, y :7
         * x : 128, y :8
         * x : 136, y :9
         * x : 145, y :10
         * result : 155
         */

    }

}

 

 

 

🔗 참고 및 출처

https://dororongju.tistory.com/137

반응형
반응형

📝Optional

개발할 때 가장 많이 발생하는 예외중 하나는 NPE로서 NPE를 피하려면 null 여부 검사를 해야하는데 코드가 복잡하고 번거롭다 Java8에서 나온 Optional<T> 클래스를 이용해 NPE를 방지할 수 있도록 도와준다.

 

  • 참고로 자바스크립트에서는 Optional사용시 null이나 undefinedundefined를 반환시킨다 [Null에 대한 에러 발생 핸들링]

 

 

Optional 사용해보기

package hello.exception;

import java.util.Optional;

public class Main {
    public static void main(String[] args) throws Exception{

        /** ---- optional 값 체크 ---- **/
        Optional<String> optVal0 = Optional.of("abc");
        Optional<String> optVal2 = Optional.ofNullable(null); // Null 체크 NPE 발생 X
        // Optional<String> optVal1 = Optional.of(null);      // NullPointerException 발생

        System.out.println(optVal2);
        // hello 값이 있는 경우 : Optional[hello]
        // null인 경우         : Optional.empty

        /** ---- optional 초기화 ---- **/
        Optional<String> optVal3 = null;             // 사용 금지!!
        Optional<String> optVal4 = Optional.empty(); // 정상 초기화 작업

        /** ---- optional 값 체크해서 Null 핸들링 하기 ---- **/
        Optional<String> optVal = Optional.ofNullable("test");
        String str1 = optVal.get();                                  // optVal에 저장된 값 반환, Null이면 예외 발생
        String str2 = optVal.orElse("");                             // optVal에 저장된 값 반환, Null이면 orElse안에 값 반환    [많이 사용] | 매개변수 : T
        String str3 = optVal.orElseGet(()->"");                      // optVal에 저장된 값 반환, Null이면 orElseGet안에 값 반환 [많이 사용] | 매개변수 : Supplier
        String str4 = optVal.orElseThrow(NullPointerException::new); // null이면 false, 아니면 true를 반환

        // ifPresent -> null이 아닐 때만 수행한다
        Optional.ofNullable("notNull").ifPresent(System.out::println);

    }

}

Optional이라는게 NPE를 체크하기 위해서 사용하는데 Optional.of의 경우 이미 null이 아니라는 걸 알고 있어야 쓸 수 있기 때문에 Optional.ofNullable을 사용합니다.

 

  • Optional.of
    • null이 아닌 값을 감싸는 Optional 객체를 생성합니다. 인자가 null이면 NullPointerException을 던집니다.
  • Optional.ofNullable
    • 값이 null일 수 있는 경우 사용합니다. 값이 null이면 빈 Optional 객체를 반환합니다.
  • Optional.empty()
    • 빈 Optional을 반환합니다. null을 넣는게 아니라 빈 Optional을 넣어서 초기화 시킵니다.
  • orElseThrow
    • null이 아닌 경우는 해당 값을 반환하고 null인 경우 Supplier가 제공하는 예외를 던집니다.
  • ifPresent
    • 값이 존재하면 true, 그렇지 않으면 false를 반환합니다. 만약 ifPresent의 매개변수 값에 함수가 들어간 경우 Null이 아닐 때 실행되게 됩니다.

 

orElse vs orElseGet 

package hello.exception;


import org.junit.jupiter.api.Test;

import java.util.Optional;

public class orElseTest {

    @Test
    public void testOptionalElse(){

        String notNull ="not Null";
        String nulll = null;

        String resultNullOrElse = Optional.of(notNull).orElse(welcome());           // welcome 함수 실행
        String resultNotNullOrElse = Optional.ofNullable(nulll).orElse(welcome());  // welcome 함수 실행

        System.out.println("notNullOrElse : " + resultNullOrElse); // notNullOrElse : not Null
        System.out.println("NullOrElse : " + resultNotNullOrElse); // NullOrElse : WelComeTo Blog

        System.out.println("===============================");

        String resultNullOrElseGet = Optional.of(notNull).orElseGet(this::welcome);           // doesn't call welcome
        String resultNotNullOrElseGet = Optional.ofNullable(nulll).orElseGet(this::welcome);  // welcome 함수 실행

        System.out.println("notNullOrElseGet : " + resultNullOrElseGet); // notNullOrElseGet : not Null
        System.out.println("NullOrElseGet : " + resultNotNullOrElseGet); // NullOrElseGet : WelComeTo Blog

        /**
         * ---- 출력결과
         * welcome!!
         * welcome!!
         * notNullOrElse : not Null
         * NullOrElse : WelComeTo Blog
         * ===============================
         * welcome!!
         * notNullOrElseGet : not Null
         * NullOrElseGet : WelComeTo Blog
         *
         */

    }

    public String welcome(){
        System.out.println("welcome!!");
        return "WelComeTo Blog";
    }
}

null도 아닌데 실행시키는 경우 말고는 orElseGet을 대체적으로 사용합니다.

 

  • orElse
    • (T other)로 값을 받는데 얘는 Eager Evaluation으로 동작하기 때문에 Null이든 말든 orElse의 값이 있으면 실행하게 됩니다.
  • orElseGet
    • Supplier의 get을 이용하는데 Supplier의 get이 Lazy Evaluation으로 동작하기 때문에 결과가 Null 이여야지만 동작하게 됩니다. 그래서 Null인 경우 아예 동작을 하지 않는 것이죠

 

 

🔗 참고 및 출처

https://erjuer.tistory.com/102

반응형
반응형

 

📝BigDecimal

Java 언어에서 숫자를 정밀하게 저장하고 표현할 수 있는 유일한 방법이다.
소수점을 저장할 수 있는 가장 크기가 큰 타입인 double은 소수점의 정밀도에 있어 한계가 있어 값이 유실될 수 있다.
Java 언어에서 돈과 소수점을 다룬다면 BigDecimal은 선택이 아니라 필수이다

 

BigDecimal 단점

  • 느린 연산 처리 속도
  • 기보타입에 비해 많은 양의 리소스 차지
  • 기본 타입보다 조금 불편한 사용법

 

double vs BigDecimal

  • double 타입
    • 내부적으로 수를 저장할 때 이진수의 근사치를 저장한다 (64비트 부동 소수점 자료형)
  • BigDecimal 타입
    • 내부적으로 수를 십진수로 저장하여 아주 작은 수과 큰 수의 연산에 대해 거의 무한한 정밀도를 보장한다

 

package hello.exception;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

public class study {

    public static void main(String[] args) {

        BigDecimal bigDecimalA = new BigDecimal("10.0000000000");
        BigDecimal bigDecimalB = new BigDecimal("3.000000000");

        DecimalFormat decimalFormat = new DecimalFormat("0.0000000000000000000000");

        double a = 10.0000000000;
        double b = 3.000000000;

        /** a / b -> 기대값: 3.333333333333 **/

        System.out.println(decimalFormat.format(a / b)); // 출력값 : 3.3333333333333335000000


        // 20자리까지 표현하고 그 이후에는 반올림을 한다
        BigDecimal result = bigDecimalA.divide(bigDecimalB, 20, RoundingMode.HALF_UP);
        System.out.println(result); // 출력값 : 3.33333333333333333333


    }
}

 

🔗 참고 및 출처

https://jsonobject.tistory.com/466#:~:text=BigDecimal%EC%9D%80%20Java%20%EC%96%B8%EC%96%B4%EC%97%90%EC%84%9C,%EC%9D%B4%20%EC%9C%A0%EC%8B%A4%EB%90%A0%20%EC%88%98%20%EC%9E%88%EB%8B%A4.

반응형