[whiteship] 5주차 - 클래스

노력을 즐기는 사람·2020년 12월 21일
0
post-thumbnail

과제 (Optional)

  • int 값을 가지고 있는 이진 트리를 나타내는 Node라는 클래스를 정의하세요.
  • int value, Node left, right를 가지고 있어야 합니다.
  • BinaryTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node node) 메소드를 구현하셍.
  • DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.

Class

자바의 프로그램은 모두 클래스로 이루어져 있다. 클래스는 자바를 구성하는 가장 기본적인 구성 요소이다. 따라서 자바 클래스를 벗어나서는 절대로 코드를 작성할 수 없다

클래스는 Java에서 제공하는 5개의 reference type 중 하나이다. 하지만 가장 중요하다고 할 수 있다. Class에서 가장 중요한 내용은 새로운 데이터 타입을 정의할 수 있다는 내용이다.

reference type의 종류
1. Annotation
2. Array
3. Class
4. Enumeration
5. Interface

클래스를 정의하자면 값을 저장하고 있는 데이터 타입의 집합체이며 그 데이터 타입들을 연산하는 메소드들의 집합체이다.
이 클래스의 인스턴스(instance)객체(object)라고 부른다.

그래서 C언어를 공부한 후 Java를 처음으로 배웠을 때 구조체랑 비슷한 녀석이구나 하는 생각이 들었다. _Class 정의를 예시로 들 때 자주 Point 클래스를 정의하는데 이건 관습인가?
어쨌든 x,y 의 좌표를 표현할 수 있는 Point 클래스를 정의해보자

Defining a Class

클래스 정의는 signaturebody로 구성된다.

  • signature: 클래스명과 같은 중요한 정보를 특정한다.
  • body: field, method, constructor, initializers, nested types 을 포함한다.

한국 서적에서는 클래스안에 존재하는 변수를 멤버변수라고 부르던데 (static은 클래스 변수라고 부르더라) 영어 서적에서는 field라고 부른다.

멤버변수 정의

Point 클래스에서는 두개의 field를 선언할 것이다.

  • double x
  • double y
public class Point {
  public double x, y;
}

메소드 선언

클래스는 field 외에도 method나 또 다른class를 포함할 수 있다.
Point 클래스를 사용하여 2차원 좌표계의 점 사이의 거리를 계산하기 위해서 생성자와 메소드를 아래와 같이 추가 했다.

public class Point {
  public double x, y;

  // 메소드 정의
  public double distanceFromOrigin() {
    return Math.sqrt(x*x + y*y);
  }
}

메소드를 선언할 때 오버라이딩, 오버로딩을 사용할 수 있다.

오버로딩

오버로딩은 같은 이름의 함수를 여러 개 선언할 때 사용한다.
이름은 같지만 매개변수나 리턴타입이 달라야한다.


    ...

    // 기본형
    public double distanceFromOrigin() {
        return Math.sqrt(x*x + y*y);
    }

    // 이름은 같지만 매개변수가 다른 것
    public double distanceFromOrigin(Point p) {
        return Math.sqrt(p.x*x + p.y*y);
    }

    // 이름은 같지만 매개변수와 접근제어자가 다른 것
    // 단, 매개변수는 같으면서 접근제어자만 다를 수는 없다
    private double distanceFromOrigin(double x, double y) {
        return Math.sqrt(this.x*x + this.y*y);
    }

    // 이건 안된다는 뜻
    public double distanceFromOrigin(double x, double y) {
        return Math.sqrt(this.x*x + this.y*y);
    }
    
    // static일 수도 있음
    static public double distanceFromOrigin(Point p1, Point p2) {
        return Math.sqrt(p1.x*p2.x + p1.y*p2.y);
    }
    
    ...
    

오버라이딩

오버라이딩은 상속 받은 메소드나 구현해야하는 메소드를 재정의할 때 사용한다. 상속 받은 클래스는 부모의 private 메소드를 제외하고 모든 메소드를 상속받기 때문에 당연히 private 메소드는 오버라이딩 할 수 없다.
모든 클래스는 Object 클래스를 상속받기 때문에 toString()을 오버라이딩 해보자


    ...

    @Override
    public String toString() {
        return "X: " + x + " Y: " + y;
    }
    
    ...
  • @Override 어노테이션은 안써도 동작한다. 그러나 반드시 사용해서 오버라이딩 메소드라고 명시하자
  • 메소드 선언부(이름, 리턴타입, 매개변수)는 변경하면 안되지만 리턴 타입을 부모 클래스의 리턴 타입으로 변환할 수 있는 타입이라면 변경할 수 있다
  • 접근 제어자를 좁은 범위로 변경할 수 없다
  • 부모 클래스의 메소드보다 더 큰 범위의 예외를 선언할 수 없다

@Overrid 는 컴파일 단에서 어노테이션이 붙은 함수가 오버라이딩된 함수인지 아닌지를 검사해준다.

생성자 정의

  • 인스턴스를 생성할 때 가장 먼저 호출되는 녀석이다.
  • new 연산자에 의해서 호출된다.
  • 생성자는 여러개 정의할 수 있다. (오버로딩이 가능하다)
  • 생성자의 이름은 클래스의 이름과 같아야 한다.
  • 리턴 타입이 없다.

객체 초기화에 필요한 로직을 작성하자.

    ...

    // 다른 생성자를 생성하면 기본 생성자가 생략된다.
    Point() {
        
    }
    
    // 생성자 여러개 생성 가능
    Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    // 생성자 여러개 생성 가능 매개변수는 달라야한다.
    Point(Point p) {
        this = p;
    }
    
    ...

Creating an Object

클래스를 선언한 후에 객체를 생성해야한다. 객체를 생성할 때 항상 살펴봐야 하는 것은 생성자이다. 위에서 선언한 생성자에 맞게 객체를 생성하자

public class Point {

  // 기본 생성자 선언 안하면 없음
  Point(double x, double y)  {
    this.x = x; this.y = y;
  }
}

...
...

Point p = new Point(2.0, -3.5);
// Point p; // 기본 생성자가 없어서 이건 불가능

만약 생성자가 하나도 없다면 public Point() {} 처럼 기본 생성자를 바이트 코드에 자동으로 추가시킨다. 반면 기본 생성자 이외의 생성자를 선언했다면 기본 생성자를 생성해주지 않는다. 만약 기본 생성자가 필요하다면 따로 선언해줘야한다

new operator

객체를 생성할 때 new 연산자가 필요하다. new 연산자는 인스턴스를 생성해주는 역할을 담당하는데 내부적으로 어떤 동작을 하는지 살펴보자

  • 메모리(Heap 영역)에 데이터를 저장할 공간을 할당받는다.
  • 할당 받은 공간의 참조값(reference value / 해시코드)를 인스턴스에게 반환해준다.
  • 생성자를 호출한다.

this 이해하기

public class Point {
  double x;
  Point (double x) { this.x=x; }
}

위와 같은 코드가 있을 때 this를 사용해야한다.

  • Point 클래스에는 x라는 변수가 선언되어 있다.
  • Point의 생성자는 외부에서 x라는 변수를 매개변수로 받는다.
  • 이때 외부의 x와 내부의 x가 겹치는 문제가 발생한다.

그래서 this.x라는 코드를 작성하면 유저가 생성한 Point인스턴스가 가지고 있는 x를 가리키게 된다.
만약 겹치지 않으면 this 연산자를 사용하지 않아도 상관없다.

this vs this()

this()는 같은 클래스의 다른 생성자를 호출할 때 사용한다.

    ...
    
    Point(Point p) { this(p.x, p.y); } // this()로 다른 생성자 호출
    Point(double x, double y) { this.x=x; this.y=y; }
    
    ...
profile
노력하는 자는 즐기는 자를 이길 수 없다

0개의 댓글