ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java [Lombok] @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor, field 등 개념 정리
    java 2023. 9. 26. 11:53
    반응형

    목차

    들어가기전

    도      입

    문제의식

    탐구 과정 1 : 공식 문서 검색

     1.1. @NoArgsConstructor

     1.2. @RequiredArgsConstructor

     1.3. @AllArgsConstructor

     

    탐구 과정 2 : 추가 개념 정리

     2.1. 생성자란?

     2.2.필드란?

     2.3.final이란?

     2.4.final필드 때문에 생성자를 생성할수 없는 경우는?

     2.5.정적 팩토리 메서드란?

     2.6.인스턴스란?

    탐구 과정 3 : 내 언어로 정리

    요약 정리

    추가수정사항

     

    들어가기전

    이 글은 @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor등의 개념에 대해 공부한 내용을 다루고 있습니다. 위 어노테이션들의 사용을 지양하는 결론이 나왔으며 대신 @Builder를 사용하는 쪽으로 방향을 잡았습니다. @Builder에 대한 내용은 다음 포스팅에서 다룰 예정입니다.

     

    도입

     

    어느순간 관성적으로 사용하던 어노테이션에 의문이 들었다.

    이전까지는 우선 이해가 되지 않아도 앞으로 나가는 것이 중요했다. 잘 몰라도 우선 해보자의 마인드였다.

    이젠 각각의 어노테이션은 왜 사용되는거고 왜 필요한지 개념정리를 해야함의 필요성을 느꼈다.

     

    문제 의식

     

    Entity 클래스에 대한 리팩토링을 하던 중 @NoArgsConstructor 등의 어노테이션이 붙어있었다.

    처음 코드를 작성할 때는 급하게 진행하느라 생성자에 대한 어노테이션이라는 것만 얼추알고 진행했다.

    다시 확인해보니 상황에 맞게 수정해야겠다는 생각이 들었다. (setter의 수정 필요성에 대한 내용은 다른 포스팅에서 다루겠다.)

     

    탐구 과정 1 : 공식 문서 검색

     

    Lombok공식 홈페이지에 정리된 내용을 찾았다.

    영어로 되어 있어서 번역기를 돌려서 보았다. 읽어보며 대충은 알아도 정확하게 모르는 것을 체크하였다.

     

     이 세 가지 주석(@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor)은 주문에 맞게 생성자를 생성하는데 사용됩니다. 이 생성자들은 특정 필드마다 하나의 매개변수를 받고, 이 매개변수를 해당 필드에 할당하는 생성자를 생성합니다.

     

     1. @NoArgsConstructor: 이 주석은 매개변수가 없는 생성자를 자동으로 생성합니다. 그러나 만약 (final 필드 때문에) 이 생성자를 생성할 수 없다면, 컴파일러 오류가 발생할 것입니다. @NoArgsConstructor(force = true)를 사용하면 모든 final 필드가 0, false 또는 null로 초기화됩니다. @NonNull과 같은 제약 조건이 있는 필드에 대해서는 체크를 생성하지 않으므로 이러한 제약 조건은 해당 필드가 나중에 올바르게 초기화될 때까지 일반적으로 충족되지 않을 수 있습니다. 일부 자바 구조물, 예를 들어 하이버네이트와 서비스 제공자 인터페이스는 매개변수 없는 생성자를 필요로 합니다. 이 주석은 주로 @Data 또는 다른 생성자 생성 주석과 함께 사용됩니다.

     

     2. @RequiredArgsConstructor: 이 주석은 특별한 처리가 필요한 각 필드마다 1개의 매개변수를 생성하는 생성자를 생성합니다. 초기화되지 않은 final 필드는 매개변수로 받고, @NonNull로 표시된 필드 중 초기화되지 않은 필드도 매개변수로 받습니다. @NonNull로 표시된 필드에 대해서는 명시적인 null 체크도 생성됩니다. 이 생성자는 @NonNull로 표시된 필드에 null이 포함된 경우 NullPointerException을 throw합니다. 매개변수의 순서는 클래스 내에서 필드가 나타나는 순서와 일치합니다.

     

     3. @AllArgsConstructor: 이 주석은 클래스 내의 모든 필드마다 1개의 매개변수를 생성하는 생성자를 생성합니다. @NonNull로 표시된 필드는 해당 매개변수에 대한 null 체크를 생성합니다

    이러한 주석은 항상 생성된 생성자를 private로 만들고 추가로 생성된 private 생성자를 감싸는 추가적인 정적 팩토리 메서드를 생성하는 대체 형태를 허용합니다. 

     

    탐구 과정 2 : 추가 개념 정리

     

     생성자란?

     

     생성자(Constructor)는 객체 지향 프로그래밍(OOP)에서 클래스로부터 객체(인스턴스)를 생성할 때 호출되는 특수한 메서드입니다. 생성자의 주된 역할은 객체의 초기화(initialization)를 담당하는 것입니다. 객체가 생성될 때 필요한 초기 작업을 수행하며, 객체가 사용될 준비를 마치게 합니다.

     

    주요 특징과 개념:

    1. 객체 초기화: 생성자는 객체를 생성하면서 필드를 초기화하거나 객체의 상태를 설정합니다. 이러한 초기화는 객체의 필드에 값을 할당하거나 다른 초기화 작업을 수행할 때 사용됩니다.

    2. 메서드와 구분: 생성자는 일반적인 메서드와 구분하기 위해 특별한 규칙을 따릅니다. 주로 클래스의 이름과 동일하며 반환 타입을 가지지 않습니다. 생성자는 new 키워드와 함께 호출되며, 객체가 생성될 때 자동으로 실행됩니다.

    3.오버로딩 가능: 하나의 클래스에 여러 개의 생성자를 정의할 수 있으며, 이를 생성자 오버로딩(Overloading)이라고 합니다. 서로 다른 매개변수를 가진 생성자를 정의하여 다양한 초기화 옵션을 제공할 수 있습니다.

    4.기본 생성자: 클래스에 명시적인 생성자가 정의되지 않으면 기본 생성자(default constructor)가 자동으로 제공됩니다. 기본 생성자는 매개변수가 없고 아무 동작도 하지 않습니다.

    5.매개변수를 통한 초기화: 생성자는 매개변수를 통해 객체의 필드 값을 설정할 수 있습니다. 이를 통해 다양한 값으로 객체를 초기화할 수 있습니다.

    6.객체 생성 시 자동 호출: 객체를 생성할 때 new 연산자와 함께 생성자가 호출됩니다. 생성자가 호출되면 객체의 메모리가 할당되고 초기화 작업이 수행됩니다.

     

    필드란?

     

     "필드"는 객체 지향 프로그래밍(OOP)에서 객체의 상태를 나타내는 변수(variable)를 의미합니다. 객체는 데이터와 메서드로 구성되며, 데이터는 객체의 상태를 나타내는데 사용됩니다. 이러한 데이터는 주로 클래스 내부에서 선언된 변수로 표현되며, 이러한 변수를 필드(field)라고 부릅니다.

     필드는 클래스의 멤버 변수로 클래스의 모든 메서드에서 접근할 수 있습니다. 필드는 객체의 고유한 속성을 저장하거나 객체 간의 상태를 유지하는 데 사용됩니다. 예를 들어, 자동차 클래스의 객체에서 "색상", "속도", "연료량" 등은 필드로 나타낼 수 있습니다.

    예를들어 color, speed, fuelLevel은 Car 클래스의 필드입니다. 이러한 필드는 객체가 생성될 때 메모리에 할당되고, 객체의 상태를 저장하는 데 사용됩니다. 필드는 객체의 특성을 나타내며, 이러한 필드에 저장된 데이터는 객체가 다루는 정보를 나타냅니다.

    필드는 일반적으로 클래스 내부에서 접근 제한자(private, protected, public)와 함께 사용되며, 객체의 캡슐화(encapsulation) 개념을 구현하는 데 도움을 줍니다. 이는 필드의 접근을 제어하여 객체의 데이터를 보호하고, 유효성 검사 및 다양한 연산을 적용할 수 있도록 합니다.

     

    final이란?

     

     final 키워드는 다양한 컨텍스트에서 사용되며, 그 중 하나는 변수나 필드에 사용될 때입니다. final로 선언된 필드는 특별한 속성을 갖습니다.

    1.값 변경 불가능(Immutable): final 필드는 한 번 초기화되면 값을 변경할 수 없습니다. 즉, 한 번 값을 할당하면 그 이후에는 해당 필드의 값을 수정할 수 없습니다.

    2.반드시 초기화: final 필드는 선언과 동시에 초기화되어야 합니다. 클래스 생성자나 초기화 블록에서 초기화해도 됩니다. 이렇게 초기화한 후에는 더 이상 값을 변경할 수 없습니다.

    3.스레드 안전성: final 필드는 멀티스레드 환경에서 안전합니다. 여러 스레드가 동시에 final 필드에 접근하더라도 그 값을 변경하지 않기 때문에 스레드 간 경쟁 문제가 발생하지 않습니다.

    4.상수로 사용: final 필드는 상수(constant)로 사용할 때 유용합니다. 예를 들어, 수학적인 상수나 설정값을 나타내는데 final 필드를 활용할 수 있습니다.

    5.코드 가독성과 안정성: final 필드는 코드의 가독성을 높이고, 실수로 값이 변경되는 것을 방지해 안정성을 향상시킵니다.

     

    final필드 때문에 생성자를 생성할수 없는 경우는?

     

     모든 final 필드를 생성자에서 초기화하지 않았을 때: final 필드는 반드시 객체가 생성될 때 초기화되어야 합니다. 그러나 모든 final 필드를 생성자에서 초기화하지 않았을 때, 컴파일러는 생성자를 생성할 수 없다는 오류를 발생시킵니다. 이는 final 필드 중 하나 이상을 초기화하지 않은 경우에 해당합니다.

     

    정적 팩토리 메서드란?

     

     정적 팩토리 메서드(Static Factory Method)는 객체를 생성하고 반환하는 메서드입니다. 이 메서드는 해당 클래스 내부에 정의되며, 객체를 생성하는 데 사용됩니다. 정적 팩토리 메서드는 객체 생성을 위한 다른 방법으로 생성자(Constructor)를 대체할 수 있습니다.

    정적 팩토리 메서드의 주요 특징과 이점은 다음과 같습니다:

    1. 이름을 가질 수 있음: 생성자와 달리 정적 팩토리 메서드는 이름을 가질 수 있습니다. 이렇게 명명된 메서드는 어떤 종류의 객체를 생성하는지 명확하게 나타낼 수 있습니다.

    2. 호출 시마다 같은 객체 반환 가능: 정적 팩토리 메서드는 필요에 따라 매번 새로운 객체를 생성하는 대신, 이미 생성된 객체를 반환할 수 있습니다. 이를 통해 객체의 재사용이 가능하며, 이는 성능을 향상시키는 데 도움이 됩니다.

    3. 하위 클래스 반환 가능: 정적 팩토리 메서드는 자식 클래스의 인스턴스를 반환할 수 있으므로, 하위 클래스의 생성 로직을 캡슐화하고 클라이언트 코드에서는 슈퍼 클래스의 타입으로만 다룰 수 있습니다.

    4. 메서드 오버로딩: 다양한 시그니처를 가진 정적 팩토리 메서드를 여러 개 정의할 수 있으므로, 다양한 매개변수 조합에 따라 다른 객체를 생성할 수 있습니다.

    5. 공유된 객체 풀: 정적 팩토리 메서드를 사용하여 객체를 생성 및 관리할 때, 객체 풀(Object Pool) 패턴을 구현할 수 있습니다. 이로써 매번 객체를 생성하는 비용을 절약하고 성능을 최적화할 수 있습니다.

    6. 암시적 타입 변환: 정적 팩토리 메서드를 사용하면 암시적 타입 변환이 가능합니다. 즉, 클라이언트 코드에서 명시적으로 타입 캐스팅을 할 필요 없이 메서드 호출 결과를 사용할 수 있습니다.

    일반적으로 정적 팩토리 메서드는 클래스 내부에서 private 또는 protected로 선언되어 외부에서 직접 호출할 수 없도록 만들어집니다. 클라이언트 코드는 이러한 정적 팩토리 메서드를 통해 객체를 생성하고, 이를 통해 객체 생성 로직을 캡슐화하고 더 유연한 객체 생성을 가능하게 합니다.

     

    인스턴스란?

     

     "인스턴스(Instance)"는 객체 지향 프로그래밍(OOP)에서 클래스의 실제 예제를 나타내는 것을 말합니다. 클래스는 일종의 설계 도면이며, 이를 기반으로 실제로 메모리에 할당된 데이터를 가진 객체(인스턴스)를 생성할 수 있습니다.

    주요 개념:

    1. 클래스: 클래스는 객체를 생성하기 위한 템플릿 또는 설계 도면입니다. 클래스는 객체의 속성(멤버 변수)과 행동(메서드)을 정의하며, 객체를 생성하기 위한 기본 구조를 제공합니다.

    2. 객체(인스턴스): 클래스를 기반으로 생성된 실제 데이터를 가지는 것을 객체 또는 인스턴스라고 합니다. 객체는 클래스의 인스턴스이며, 클래스에 정의된 속성과 메서드를 사용할 수 있습니다.

    3. 인스턴스화(Instantiation): 클래스로부터 객체를 생성하는 과정을 인스턴스화라고 합니다. 이 과정에서는 클래스의 생성자(Constructor)를 호출하여 객체를 초기화하고 메모리에 할당합니다.

    4. 객체의 특성: 객체는 클래스에 정의된 속성(또는 필드)을 가집니다. 이러한 속성은 객체의 상태를 나타내며, 객체마다 다른 값을 가질 수 있습니다.

    5. 객체의 동작: 객체는 클래스에 정의된 메서드를 실행할 수 있습니다. 이러한 메서드는 객체가 수행하는 동작 또는 작업을 정의합니다.

    6. 객체 간 상호 작용: 객체 지향 프로그래밍에서 객체는 다른 객체와 상호 작용하며 정보를 교환합니다. 이를 통해 복잡한 시스템을 모델링하고 문제를 해결할 수 있습니다.

    예를 들어, 자동차 클래스가 있다면 이 클래스를 기반으로 실제 자동차 객체(인스턴스)를 생성할 수 있습니다. 각 자동차 객체는 고유한 속성(색상, 속도, 연료량)을 가지며, 메서드(주행, 정지, 가속)를 호출하여 동작합니다. 각 자동차 객체는 클래스로부터 생성된 인스턴스입니다.

    클래스와 객체의 개념은 OOP의 중요한 개념 중 하나이며, 현실 세계의 개념을 소프트웨어로 모델링하는 데 사용됩니다.

     

    탐구 과정 3 : 내 언어로 정리

     

    @Constructor 종류

     

    @NoArgsConstructor는 매개변수가 없는 기본생성자를 만들어주는 lombok주석이다. Jpa를 사용하여 Entity를 정의할 때 기본생성자가 필요하기 때문에 기본적으로 이 주석은 필요하다.

     이때 final 필드 때문에 생성자를 생성할 수 없어서 compiler오류가 생길 수도 있다. final 필드는 선언과 동시에 초기화 되어야 하는 필드인데, 초기화되지 않은 생성자가 있다는 것이다.

    ( 참고로 객체는 데이터와 메서드로 구성되어 있다. 데이터는 객체의 상태를 나타내는데 사용되고 이 데이터는 주로 클래스 내부에서 선언되는 변수로 표현된다. 이때의 변수를 필드라고 부른다. )

    @RequiredArgsConstructor는 필수로 초기화를 해야하는 필드가 있을 때 주로 사용한다. 보통 @NonNull과 같이 사용된다.

    예를 들어서 @NonNull로 설정한 필드가 있을 때 이 필드는 초기화를 무조건 해야한다. @RequiredArgsConstructor를 쓰면 이를 편리하게 수행할 수 있다. 즉, @NonNull로 설정한 필드에는 초기화를 해주고, 만약 다른 필드가 있다면 선택적으로 초기화 할 수 있다. (초기화 하지 않는다면 기본 0으로 초기화 된다.)

    반면에 @AllArgsConstructor를 사용할 경우 모든 필드의 초기화가 필요할 경우 또는 선택적으로 초기화 할 필요가 없는 경우 사용된다.

    @AllArgsConstructor를 사용하면 모든 필드의 초기화가 자동으로 수행된다.

     

    인스턴스와 객체

    인스턴스(Instance)와 객체(Object)는 대부분 동일한 의미를 가지는 용어다.

    즉, 객체 지향 프로그래밍(OOP)에서 클래스를 기반으로 생성되어 메모리에 할당된 데이터 구조를 말한다.

     

    정적 팩토리 메서드와 생성자

     정적 팩토리 메서드는 정적 메서드로, 클래스의 인스턴스를 생성하고 반환한다. 객체 생성을 추상화하고 명확한 이름을 가질 수 있다.

    상속 및 인터페이스 기반 프로그래밍에 유용하고, 여러 생성자 오버로딩에 따른 혼동을 줄일 수 있다.(이름을 가질 수 있어서)

     생성자는 특수한 메서드로, 객체를 초기화하고 인스턴스를 생성한다. 객체 생성 시에는 new키워드와 함께 생성자를 호출한다. 이름이 고정되어 있고 별도의 이름을 가지지 않는다. 주로 단순한 객체 생성에 사용되고, 객체 생성과 초기화가 동시에 이루어진다.

     요약 정리

    1. @NoArgsConstructor는 Jpa를 사용하여 Entity를 정의하는 상황에서 필요하다.

    2. @RequiredArgsConstructor는 @NonNull가 적용된 필드의 초기화를 직접 해주기 위해 사용된다고 볼 수 있다.

    3. @AllArgsConstructor는 모든 필드의 자동 초기화가 되기 때문에 @NonNull이 없을 경우 간편하게 사용할 수 있다고 볼 수 있다.

     

    ------

    +추가 수정 사항(중요)

     

    위와 같이 정리를 하고 추가로 공부를 하다보니 기존의 공부 내용을 싹다 뒤집어야 할 정도로 허점이 많았다는 것을 깨달았다.

     

    1. @RequiredArgsConstructor, @AllArgsConstructor 는 가능한 사용을 지양하는 것이 좋다는 것이다.

     치명적인 단점으로 개발자가 필드 생성자의 순서를 바꿔줄 경우 적용되는 값이 완전히 바뀔 수 있다는 내용이 있다.

    2. @setter의 경우 예상치못한 수정등의 이유로 사용을 지양하는데 @AllArgsConstructor를 사용하면 @setter를 사용한것과 같은 문제가 생길 수 있다.

    3.@Builder를 사용할 경우 @NoArgsConstructor 조차도 필요하지 않다.

     

    @Builder에 대한 내용은 다음 포스팅을 통해 자세히 다루도록 하겠다.

     

     

     

     

    -----

     

     

     

    반응형

    댓글

Designed by Tistory.