코드 그라데이션

230224 클래스, 생성자, this, 오버로딩 간략 정리 본문

Java, SpringBoot 추가 공부

230224 클래스, 생성자, this, 오버로딩 간략 정리

완벽한 장면 2023. 4. 4. 02:25

클래스

클래스는 하나의 데이터 타입을 만드는 것이고,

자바 컴파일러가 모든 타입을 만들어 줄 수는 없으니

현실의 문제를 풀 때마다 우리가 데이터 타입을 하나씩 만들어준다는 개념

 

클래스 하면 빼놓을 수가 없는 게 생성자

 

생성자

- 쉽게 말하면, 객체를 만들 때 불리는 함수. 

- 스프링 개념을 조금 섞자면,

  JPA에서 엔티티로 관리할 클래스는 기본 생성자가 반드시 있어야 한다.

 

- 기본 생성자는 인자도 없고 메서드 본문도 없는 소위 "깡통" 같은 것.

- 모든 클래스는 생성자를 반드시 하나는 가지고 있어야 하기 때문에 

  자바 컴파일러가 기본으로 만들어준다. 

 

- JPA에서는...

  JPA는 객체 세계와 데이터베이스 세계를 매핑해주는 아이기 때문에 기본 생성자를 제공해야 함.

 

생성자 활용

(1) 객체가 생성되자마자 반드시 해야 하는 행동이 있을 때

Board에 title이 반드시 존재해야 한다!

- 생성자가 호출될 때 매개변수로 반드시 title을 받게 하면, 호출이 된다.

(2) 객체가 반드시 가져야 하는 값이 있을 때

- 비용이 너무 비싸면, 언제 만들어지고 언제 죽는지 알아야 할 때 (로그를 찍는다)

(3) 편의를 위해

- DTO를 매번 만들고 Setter를 부르는 게 지루한 일이니까.

- 아예 생성자를 만들어서 Setter로 부르는 게 아니라 인자로 넘겨줘서 간단하게 하기 위해

 

Movie movie1 = new Movie();에서 new 키워드 뒤의 Movie()가 바로 생성자를 호출하는 코드

 

- 생성자는 리턴 타입이 없음.

- 생성자는 반드시 클래스와 이름이 같아야 함.

 

this

: this객체 자기 자신을 참조하는 변수(생략 가능)

 

- 클래스 안에서 자신의 필드나 메서드를 사용할 때 this를 사용해야 함

- 변수 이름만 보면 메서드의 매개변수인지 필드인지 구분이 가지 않으므로

  -- 가독성을 위해 사용하거나

  -- 매개변수와 이름이 같은 경우 구분하기 위해 사용

 

** 자바에서는 클래스가 필수적이고, 모든 정보(함수)들은 클래스 내부에 있다.

   따라서 모든 메서드나 필드는 클래스에 소속되니까 

   어떤 클래스에 소속되어 있는지 선언을 해줘야 하고(ex) Main.max()

   선언이 안 되어 있다는 것은 자기 자신에게 소속되어 있음을 암묵적으로 의미함.

 

# this

- 클래스가 아니라, 인스턴스를 말함.
ex) Board 클래스를 정의했다고 해서 Board 객체가 생성된 건 아니지.
public class Board {

  public static void main(String[] args) {
    
  }

}

아래까지 해줘야 객체가 생성된 것

public class Board {

  public static void main(String[] args) {
    Board board = new Board();
  }

}

 

그리고 여기 id, title, content는 static이 없는 필드이기 때문에

Board가 생길 때마다 모두 독립된 값을 갖게 된다.

public class Board {

  Long id;
  String title;
  String content;

  public Board(Long id, String title, String content) {
    this.id = id;
    this.title = title;
    this.content = content;
  }
  
  public static void main(String[] args) {

  }

}

 

그러다보니까 여기서 말하는 this는 하나의 객체를 의미하는 것!

 

지금 이러한 상황에서

public class Board {

  Long id;
  String title;
  String content;

  public Board(long id, String title, String content) {
    this.id = id;
    this.title = title;
    this.content = content;
  }

  public static void main(String[] args) {
    Board board1 = new Board();
    Board board2 = new Board();

  }

}

예를 들면,

board1의 title과 board2의 title은 완전히 다른 값이다!

 

board1에서의 this는 board1을 가리키고, 

board2에서의 this는 board2를 가리킨다는 의미.

 

this를 생략해도 되는데 굳이 쓰는 이유는

생략해버리면

- 함수에서의 매개변수인지

- 로컬변수(지역변수)인지

- 필드인지

를 구분하기 어렵다...

 

만약에...

this를 생략하여 쓴다면

public class Board {

  Long id;
  String title;
  String content;

  public Board(long id, String title, String content) {
    id = id;
    title = title;
    content = content;
  }

  public static void main(String[] args) {
    Board board1 = new Board();
    Board board2 = new Board();

  }

}

 

클래스 필드의 title도 있고, 생성자 매개변수 title도 있는데,

가장 가까운 애로 인식하기 때문에, 둘 다 매개변수 title로 인식하여

클래스에 있는 title이 안 들어간다.

 

오버로드

- 사전적 의미는 짐을 싣는 것

- 그러니까 덮어씌우는 게 아니라 더욱 쌓는 것(더 만들어지는 것)이라고 생각하면 편하다.

 

- 쉽게 말하면, 같은 이름을 가진 메서드가 여러 개라는 것.

- 그런데.. 메서드의 이름은 같으나 매개변수의 개수 또는 타입이 다른 것...!

 

- 두 개의 메서드가 완전히 같으면 컴파일러 입장에서는 무엇을 호출했는지 구분할 수가 없다.

 

- 대표적으로 생성자에 활용함.

ex.

class User {
		String id;
		String password;

    // 생성자
		public User(String id, String password) {
				this.id = id;
				this.password = password;
    }

		public User(String id) {
				this.id = id;
    }

		public User(String password) { // compile error
				this.password = password;
    }
}
// 어떤 사용자는 아이디랑 비밀번호만 존재하게 만들고 싶고,
// 어떤 사용자는 아이디만 있게 만들고 싶고
// 어떤 사용자는 비밀번호만 있게 만들고 싶다면
// 이러한 형태로 만들 수 있다.

 

앞서 this는 자기 자신을 나타내는 것이라 했음.

그런데 this에 ()를 하면 : this()

나라는 객체의 함수를 부르는 거니까 => 생성자를 부르는 것이다!

 

따라서...

- 생성자에서 다른 생성자를 호출할 때`this()`를 사용

- 생성자에서 다른 생성자를 호출할 때는 생성자 호출 코드가 가장 먼저 등장해야 함

 

class User {
		String id;
		String password;
		String third;

    // 생성자
		public User(String id, String password, String nickname) {
			this.id = id;
			this.password = password;
			this.nickname = nickname;
    }

		public User(String id) {
			this(id, null, null);
            // this.id = id;
            // this.password = null;
            // this.nickname = null;
            
    }

		public User(String password) { // compile error
			this(null, password, null);
    }
}

 

비교하자면 위에서처럼 모든 생성자를 세팅하는 게 아니라,

하나만 생성해놓고, 그것을 부른다.

그리고 생성자에 추가될 요소만 괄호 속에 집어넣어준다는 것.

 

원래대로라면

            // this.id = id;
            // this.password = null;
            // this.nickname = null;

이렇게 되어있어야 하는데

이미 구현되어 있으니까, 

나는 값만 넘기겠다.

 

=> 코드를 훨씬 간결하게 쓸 수 있으며, 가독성이 좋아짐.

 

생성자에서 다른 생성자를 호출할 때는 생성자 호출 코드가 가장 먼저 등장해야 함

이런 뜻임... 위로 올리면 에러 안 난다.

왜?
상속 개념인데,
지금 위에 있는 User는 부모가 없음,
그런데 자바에서는  명시적으로 부모가 안 되어 있으면 extends Object 임.

부모 자식 관계일 때

자식 생성자와 부모 생성자의 관계는?

class Parent{
  public Parent() {
    System.out.println("Parent");
  }
}

class Child extends Parent{
  public Child() {
    System.out.println("Child");
  }
}

public class MainClass {
  public static void main(String[] args) {
    Child child = new Child();
  }
}

이 때 실행결과를 예측해보자, 순서는 어떻게 나올까?

이렇게 나온다.

 

자식은 부모에 내가 무언가를 더하는 것이죠.

즉, 부모가 일단 만들어져야 자식이 만들어 질 수 있다.

 

따라서 내가 자식 생성자를 호출하면, 명시적으로 부모 생성자를 호출하지 않더라도 부모 생성자가 먼저 호출이 된다.

그런데 내가 어디서도 부모 생성자를 호출한 적은 없다.

 

마치 자바가 내가 생성자를 만들지 않으면 기본 생성자를 생성해 주는 것처럼, 

내가 생성자에서 아무것도 부모 생성자를 호출해주지 않으면

class Parent{
  public Parent() {
    System.out.println("Parent");
  }
}

class Child extends Parent{
  public Child() {
  super(); // 이렇게
    System.out.println("Child");
  }
}

public class MainClass {
  public static void main(String[] args) {
    Child child = new Child();
  }
}

자바 컴파일러가 알아서 부모 생성자를 호출해준다.

그래서 호출한 코드가 따로 없어도 부모가 먼저 호출이 되는 것이다.

 

그런데 

만약에

이런건 또 에러...

이유

: 부모가 먼저 호출이 되기도 전에 자식 코드가 먼저 호출이 된 상황이라.

 

728x90
Comments