sourcecode

기본 생성자와 인라인 필드 초기화

copyscript 2022. 9. 14. 22:23
반응형

기본 생성자와 인라인 필드 초기화

기본 생성자와 개체의 필드를 직접 초기화하는 것의 차이점은 무엇입니까?

다음 중 하나의 예를 다른 예보다 선호하는 이유는 무엇입니까?

예 1

public class Foo
{
    private int x = 5;
    private String[] y = new String[10];
}

예 2

public class Foo
{
    private int x;
    private String[] y;

    public Foo()
    {
        x = 5;
        y = new String[10];
    }
}

이니셜라이저는 컨스트럭터 본문보다 먼저 실행됩니다.(이니셜라이저와 컨스트럭터가 모두 있는 경우 컨스트럭터 코드가 두 번째로 실행되어 초기화된 값을 덮어씁니다.)

이니셜라이저는 항상 같은 초기값(예에서는 지정된 크기의 배열 또는 특정 값의 정수)이 필요한 경우에 적합합니다.단, 사용자에게 유리하거나 불리하게 동작할 수 있습니다.

변수를 다르게 초기화하는 생성자가 여러 개 있는 경우(즉, 값이 서로 다른 경우), 변경 사항이 재정의되어 낭비되기 때문에 초기자는 사용할 수 없습니다.

한편, 같은 값으로 초기화하는 컨스트럭터가 다수 있는 경우는, 초기화를 한 곳에 보관하는 것으로, 코드 행을 보존할 수 있습니다(또, 코드의 유지보수를 약간 용이하게 할 수도 있습니다.

Michael이 말했듯이, 취향의 문제도 있습니다. 코드를 한 곳에 보관하고 싶을 수도 있습니다.컨스트럭터가 많으면 코드는 한 곳에 있지 않기 때문에 이니셜라이저를 선호합니다.

예 1을 선호하는 이유는 코드 수가 적어도 동일한 기능이기 때문입니다(항상 양호합니다).

그것 말고는, 차이가 없다.

단, 명시적인 생성자가 있다면 모든 초기화 코드를 생성자와 필드 이니셜라이저 간에 분할하는 것보다 그 안에 넣는(그리고 체인으로 하는) 것을 선호합니다.

필드에 기본값을 지정하기 위해 필드 이니셜라이저 또는 생성자를 선호해야 합니까?

필드 인스턴스화 및 필드 게으름/눈치 인스턴스화 중에 발생할 수 있는 예외는 가독성 및 유지보수성 문제 이외의 문제에 대처하지 않습니다.
동일한 로직을 실행하고 동일한 결과를 생성하는 2개의 코드에 대해서는 최고의 가독성과 유지보수성을 가진 방법을 선호해야 합니다.

TL;DR

  • 첫 번째 또는 두 번째 옵션을 선택하는 것은 무엇보다 코드 구성, 가독성유지보수에 관한 문제입니다.

  • 선택 방법의 일관성을 유지한다(전체 애플리케이션 코드를 명확하게 한다).

  • 하여 '필드의 이니셜라이저'를 해 주세요Collection[ ] 를 방지하기 NullPointerException

  • 생성자가 덮어쓸 수 있는 필드에 필드 이니셜라이저를 사용하지 않음

  • 단일 생성자를 가진 클래스에서 필드 이니셜라이저 방법은 일반적으로 더 읽기 쉽고 덜 상세하다.

  • 컨스트럭터가 그들 사이에 결합이 없거나 매우 적은 여러 컨스트럭터가 있는 클래스에서 필드 이니셜라이저 방법은 일반적으로 더 읽기 쉽고 덜 상세하다.

  • 여러 생성자가 서로 결합하는 클래스에서는 두 가지 방법 중 어느 것도 더 낫지 않지만 선택한 방법이든 체인 생성자와 결합하는 방법이 있습니다(사용 사례 1 참조).


OP 질문

매우 간단한 코드에서는 필드 선언 시 할당이 더 나은 것처럼 보이며 실제로도 그렇습니다.

이것은 보다 장황하고 직설적입니다.

public class Foo {
    private int x = 5;
    private String[] y = new String[10];
}

컨스트럭터 방식보다 커집니다.

public class Foo{
    private int x;
    private String[] y;

    public Foo(){
        x = 5;
        y = new String[10];
    }
}

실제 특수성을 가진 실제 수업에서는 상황이 다릅니다.
사실, 마주친 특정 사항에 따르면, 한 가지 방법은 그들 중 다른 한 명 또는 다른 한 명이 선호되어야 합니다.


좀 더 상세한 예시를 제시하다

스터디 케이스 1

것부터 하겠습니다.Car이러한 점을 설명하기 위해 업데이트할 클래스입니다.
Car4개의 필드와 이들 사이에 관계가 있는 일부 생성자를 선언합니다.

1. 모든 필드에 대해 필드 이니셜라이저에 기본값을 지정하는 것은 바람직하지 않습니다.

public class Car {

  private String name = "Super car";
  private String origin = "Mars";      
  private int nbSeat = 5;
  private Color color = Color.black;
  ... 

  ... 
  // Other fields

  ... 

  public Car() {         
  }

  public Car(int nbSeat) {
      this.nbSeat = nbSeat;          
  }

  public Car(int nbSeat, Color color) {
      this.nbSeat = nbSeat;          
      this.color = color;
  }   

}

필드 선언에서 지정된 기본값을 모두 신뢰할 수 있는 것은 아닙니다.만.name ★★★★★★★★★★★★★★★★★」origin필드에는 실제로 기본값이 있습니다.

nbSeat ★★★★★★★★★★★★★★★★★」color필드는 먼저 선언에서 값이 매겨지고, 그런 다음 인수와 함께 생성자에서 덮어쓸 수 있습니다.
오류 발생 가능성이 높으며 이러한 필드 평가 방법 외에도 클래스의 신뢰성 수준이 저하됩니다.의 필드, 즉 2개의 이 없는 할 수 ?

2. 컨스트럭터를 사용하여 모든 필드에 값을 매기고 컨스트럭터 체인에 의존해도 좋다.

public class Car {

  private String name;
  private String origin;      
  private int nbSeat;
  private Color color;
  ... 

  ... 
  // Other fields

  ... 

  public Car() {  
     this(5, Color.black);
  }

  public Car(int nbSeat) {    
     this(nbSeat, Color.black);       
  }

  public Car(int nbSeat, Color color) {
      this.name = "Super car";
      this.origin = "Mars";     
      this.nbSeat = nbSeat;          
      this.color = color; 
  }   

}

이 솔루션은 중복을 생성하지 않고 모든 로직을 한 곳에서 수집하기 때문에 매우 좋습니다. 즉, 최대 수의 파라미터를 가진 컨스트럭터입니다.
여기에는 콜을 다른 컨스트럭터에 체인으로 해야 한다는 단점이 있습니다.
요?

3. 컨스트럭터가 새 값을 할당하지 않는 필드의 필드 이니셜라이저에 기본값을 지정하는 것이 더 좋지만 여전히 복제 문제가 있습니다.

★★★★★★★★★★★★★★★★★를 평가하지 않음nbSeat ★★★★★★★★★★★★★★★★★」color디폴트값이 있는 필드와 없는 필드를 명확하게 구분합니다.

public class Car {

  private String name = "Super car";
  private String origin = "Mars";      
  private int nbSeat;
  private Color color;
  ... 

  ... 
  // Other fields

  ... 

  public Car() {         
     nbSeat = 5;
     color = Color.black;
  }     

  public Car(int nbSeat) {
      this.nbSeat = nbSeat;
      color = Color.black;          
  }

  public Car(int nbSeat, Color color) {
      this.nbSeat = nbSeat;          
      this.color = color;
  }   

}

은 꽤 , 각 를 반복합니다.Car컨스트럭터 체인이 있는 이전 솔루션과 반대되는 컨스트럭터.

이 간단한 예에서는 복제 문제를 이해하기 시작할 수 있지만 조금 귀찮은 것 같습니다.
실제 상황에서는 생성자가 계산과 검증을 수행할 수 있기 때문에 중복이 매우 중요할 수 있습니다.
인스턴스화 로직을 실행하는 단일 컨스트럭터를 갖는 것은 매우 유용합니다.

따라서 마지막으로 필드 선언의 할당이 항상 생성자를 다른 생성자에게 위임하도록 하는 것은 아닙니다.

여기 개선된 버전이 있습니다.

4. 컨스트럭터 체인에 의존하여 컨스트럭터가 할당하지 않은 필드의 필드 이니셜라이저에서 기본값을 설정해도 좋다.

public class Car {

  private String name = "Super car";
  private String origin = "Mars";      
  private int nbSeat;
  private Color color;
  ... 

  ... 
  // Other fields

  ...   

  public Car() {  
     this(5, Color.black);
  }

  public Car(int nbSeat) {    
     this(nbSeat, Color.black);       
  }

  public Car(int nbSeat, Color color) {
      // assignment at a single place
      this.nbSeat = nbSeat;          
      this.color = color;
      // validation rules at a single place
         ...
  }   

}

스터디 케이스 2

.Car
이제.Car5개의 필드와 3개의 컨스트럭터 사이에 관계가 없는 것을 선언한다.

1. 생성자를 사용하여 필드의 값을 기본값으로 지정하는 것은 바람직하지 않습니다.

public class Car {

  private String name;
  private String origin;      
  private int nbSeat;
  private Color color;
  private Car replacingCar;
  ... 

  ... 
  // Other fields

  ... 

  public Car() {         
    initDefaultValues();
  }

  public Car(int nbSeat, Color color) {
     initDefaultValues();
     this.nbSeat = nbSeat;         
     this.color = color;
  }

  public Car(Car replacingCar) {         
     initDefaultValues();
     this.replacingCar = replacingCar;         
     // specific validation rules          
  }

  private void initDefaultValues() {
     name = "Super car";
     origin = "Mars";      
  }

}

이를 하지 않기 때문에name ★★★★★★★★★★★★★★★★★」origin 컨스트럭터에 의해 , 그에는 다른가 포함되어 않습니다.initDefaultValues()메서드 및 호출을 수행합니다.그래서 우리는 이 방법을 부르는 것을 잊지 말아야 한다.
「」는 인라인 할 수 해 주세요.initDefaultValues() arg constructor에서는 body를 호출하지 않습니다.this() 수 .

public class Car {

  private String name;
  private String origin;      
  private int nbSeat;
  private Color color;
  private Car replacingCar;
  ... 

  ... 
  // Other fields

  ... 

  public Car() {         
     name = "Super car";
     origin = "Mars";     
  }

  public Car(int nbSeat, Color color) {
     this();
     this.nbSeat = nbSeat;         
     this.color = color;
  }

  public Car(Car replacingCar) {         
     this();
     this.replacingCar = replacingCar;         
     // specific validation rules          
  }

}

2. 컨스트럭터가 새 값을 할당하지 않은 필드의 필드 이니셜라이저에 기본값을 지정해도 됩니다.

public class Car {

  private String name = "Super car";
  private String origin = "Mars";      
  private int nbSeat;
  private Color color;
  private Car replacingCar;
  ... 

  ... 
  // Other fields

  ... 

  public Car() {         
  }

  public Car(int nbSeat, Color color) {
      this.nbSeat = nbSeat;         
      this.color = color;
  }

  public Car(Car replacingCar) {         
     this.replacingCar = replacingCar;         
     // specific validation rules          
  }

}

'아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아.initDefaultValues()노아그필드 이니셜라이저는 완벽합니다.


결론

어떤 경우에도 필드 이니셜라이저의 필드 값은 모든 필드에 대해 수행할 수 없으며 생성자에 의해 덮어쓸 수 없는 필드에 대해서만 수행해야 합니다.

활용 사례 1) 여러 개의 시공자가 공통으로 처리되는 경우, 주로 의견 기반이다.
솔루션 2(컨스트럭터를 사용하여 모든 필드에 값을 매기고 컨스트럭터 체인에 의존)와 솔루션 4(컨스트럭터가 새로운 값을 할당하지 않고 컨스트럭터 체인에 의존하는 필드의 필드 이니셜라이저에 기본값을 부여함)는 가장 가독성이 높고 유지보수가 용이하며 견고한 솔루션으로 보입니다.

사용 예 2) 단일 컨스트럭터의 경우와 같이 공통의 처리/관계가 없는 여러 컨스트럭터의 경우 솔루션 2(컨스트럭터가 새 값을 할당하지 않은 필드의 필드 이니셜라이저에 기본값 부여)가 더 좋습니다.

필드 이니셜라이저를 선호하며 복잡한 초기화 로직이 있는 경우(예를 들어 맵을 채우는 경우, 일련의 경험적 단계를 통해 한 ivar를 다른 ivar에 의존하는 경우 등) 기본 생성자를 사용합니다.

@Michael B는 말했다:

모든 초기화 코드를 컨스트럭터와 필드 이니셜라이저로 분할하는 것보다 그 안에 삽입(그리고 체인)하는 것을 선호합니다.

MichaelB(71+K rep에게 인사)는 일리가 있지만, 인라인 최종 이니셜라이저에 심플한 초기화를 유지하고, 초기화의 복잡한 부분을 컨스트럭터에서 하는 것이 제 성향입니다.

제가 생각할 수 있는 유일한 차이점은 만약 당신이 다른 컨스트럭터를 추가한다면

public Foo(int inX){
x = inX;
}

첫 번째 예에서는 기본 컨스트럭터를 사용할 수 없습니다.두 번째 예에서는 기본 컨스트럭터를 사용할 수 있습니다(또한 새로운 컨스트럭터 내부에서 호출할 수도 있습니다).

언급URL : https://stackoverflow.com/questions/4916735/default-constructor-vs-inline-field-initialization

반응형