경주장

5주차 과제: 클래스 #5 (~02.15) 본문

JAVA/whiteship_live-study

5주차 과제: 클래스 #5 (~02.15)

달리는치타 2022. 2. 8. 21:39

학습할 것 (필수)

  • 클래스 정의하는 방법
  • 객체 만드는 방법 (new 키워드 이해하기)
  • 메소드 정의하는 방법
  • 생성자 정의하는 방법
  • this 키워드 이해하기

 

졸업과 이사, Programmers Backend 스터디 프로그램 등이 겹쳐 최근 live-study의 진행이 밀리고 있습니다. ㅠㅠ

기선님의 live-study는 이미 종료된 프로그램이지만 따로 진행하는 것인 만큼 혼자 진행 할 수 있다는 장점을 살려 느리지만 꼭 완수 할 수 있도록 하겠습니다.


클래스(class)란 객체지향의 추상화(abstraction)라는 개념을 직접 구현한 것이라 할 수 있습니다.

 

클래스는 필드(상태)와 메소드(행동)로 이루어지며 다음과 같이 선언합니다.

접근제어자 class 클래스이름 {

    접근제어자 필드1의타입 필드1의이름;
    접근제어자 필드2의타입 필드2의이름;
    ...

    접근제어자 메소드1의 원형
    접근제어자 메소드2의 원형
    ...
};

 

사용자를 의미하는 User 클래스를 간단하게 구성해보겠습니다.

package week5.study;

public class Users {

    private String username;
    private int age;

    public boolean hasLastname(String lastname){
        return username.startsWith(lastname);
    }

    public boolean isTeenage(){
        return age>=10&&age<20;
    }
}

 

 

사용자의 이름과 나이를 가진 클래스입니다.  하지만 이렇게만 정의한 클래스는 전혀 활용할 수가 없습니다.
왜냐하면 클래스의 private field인 username과 age에 접근할 수 있는 방법이 전혀 작성되어 있지 않기 때문입니다.


private field인 username을 외부에서 접근하려고 하면 intelij는 아래와 같이 에러를 알려줍니다.

'username'필드는 'private'접근제어자를 가지고 있으니 외부에서 설정할 수 없는 에러입니다. 이를 해결하기 위한 방식으로 아래와 같은 대안을 제시해줍니다.

모두 Users.username필드의 접근제어자를 수정하는 것에 대한 제안이며 각각 아래와 같은 변경을 발생시킵니다.

채택한 대안 변경내용 적용된 접근제어자
Create field 'username' in 'Users' public String username;
private String username;
public
Make 'Users.username' package-private String username; default
Make 'Users.username' protected protected String username; protected
Make 'Users.username' public public String username; public

 

접근 지정자의 종류와 기능은 아래와 같습니다.

접근 지정자 클래스 내부 동일 패키지 하위 클래스 그 외
public O O O O
protected O O O X
default (생략가능) O O X X
private O X X X

아래로 갈수록 더 strict한 접근을 요구합니다. 제가 accessSpecifierTest를 작성한 곳은 test디렉토리의 week5.study.UsersTest 클래스 내부입니다. JUnit의 테스트 디렉토리의 패키지는 src디렉토리의 패키지와 같은 위치에 있는 것으로 인식합니다. 때문에 username 필드에 default 즉 package-private한 방식의 접근제어자 적용되어도 week5.study.UsersTest.accessSpecifierTest에서 접근 할 수 있게 됩니다.

 

참고로 자바의 default 접근제어자의 동일 패키지 정책은 하위 패키지와는 달리 정확히 일치하는 패키지를 의미합니다. week5.study.mypackage 패키지를 만들고 내부에서 default 접근제어자로 선언된 Users 클래스의 필드를 접근 할 수 없습니다.

default 접근제어자는 하위 패키지에서도 접근 불가!


그렇다면 여러 패키지나 클래스에서 접근 할 수 있도록 필드의 집근지정자를 모두 public으로 열어두는 것이 좋을까요?이것은 클래스의 캡슐화를 깨는 행위로 위와같은 상황에서의 적절한 해결방법은 아닙니다. field의 접근지정자는 private으로 닫아두고 setter,getter 메서드 혹은 생성자를 통해 private filed에 접근하는 것이 적절한 해결방법입니다. 생성자를 통해 객체의 상태를 지정하는 방법은 두번째 학습목표인 new 키워드 이해하기 에서 자세히 다루고 이번에는 getter와 setter를 활용하는 방식에 대해 알아보겠습니다.

 

모든 필드의 접근지정자를 public으로 열어둔다면

users.username = "홍길동";
String username = users.username;

과 같은 간단한 방식으로 users객체의 상태를 변경하고 조회할 수 있습니다. 하지만 이는 users 객체의 자율성을 회손하는 방식입니다. users 객체의 상태를 변경할 책임은 자신에게 두는것이 객체지향의 방법론에 따라 옳습니다. 위와 같은 방식에서 users객체의 상태를 변경할 책임은 코드가 작성된 클래스에 있습니다. (e.g. accessSpecifierTest) 


getter와 setter는 특별한 것이 아닙니다. 필드의 상태를 변경하고 조회하기 위한 메서드일 뿐입니다. username과 age 필드에 getter와 setter method를 선언하면 아래와 같은 class의 구성이 완성됩니다.

package week5.study;

public class Users {

    private String username;
    private int age;

    public boolean hasLastname(String lastname){
        return username.startsWith(lastname);
    }

    public boolean isTeenage(){
        return age>=10&&age<20;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

private 접근지정자를 통해 필드의 직접적인 접근을 막아두고 이를 변경, 조회 할 책임은 오로지 Users 클래스 자신에게 있습니다. 이를 통해 객체지향의 캡슐화에 한층 더 가까워 졌습니다. getter와 setter를 활용해 위의 코드를 변경하면 아래와 같습니다. 

하지만 무조건적으로 field를 private으로 막고 getter와 setter를 활용하는 것이 정답이 아닙니다.

상황에 따라 적절한 접근지정자를 활용하는것으로 좋은 클래스를 만들 수 있습니다.


접근지정자 외에도 클래스의 선언시에는 다양한 키워드들이 활용될 수 있습니다.

 

자바의 기본 클래스인 java.lang.Integer 클래스의 선언부는 아래와 같습니다.

public final class Integer extends Number implements Comparable<Integer>
  • public : 클래스의 접근 지정자를 설정한 부분입니다. Integer는 어디서든 클래스를 사용할 수 있습니다.
  • final : 해당 클래스는 재정의할 수 없습니다. 다른 말로는 상속이 불가능합니다.
  • class : 클래스로 정의한 것입니다.
  • Integer : 클래스의 명을 Integer로 설정합니다.
  • extends : Number 클래스를 상속받아서 사용합니다. (다중상속 불가)
  • implements : Complarable<Integer>의 인터페이스를 구현합니다. ( 다수 설정 가능 )

 

 

 

과제 (Optional)

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

GitHub - ndy2/live-study-assignment

Contribute to ndy2/live-study-assignment development by creating an account on GitHub.

github.com