본문 바로가기
java/자바의 정석

자바의 정석 - 객체지향 프로그래밍 6편

by choi-dev 2024. 2. 21.

5편까지 진행했고 6편을 하겠다.

 

접근 제어자

우리가 자바에서 메소드나 클래스 옆에 붙여서 사용했다. 원래는 의미가 있지만 우리는 클래스에 대한 학습을 진행하고 있었으니 public으로 통일했었다. 접근 제어자를 통해서는 외부에서 멤버 또는 클래스를 접근하지 못하도록 제한한다.

 

총 4가지를 확인할 수 있고 private, default, protected, public으로 되어있다. 왼쪽부터 오른쪽 순서로 접근이 유해진다고 보면 된다. private은 동일 클래스 내에서만 접근이 가능하고 default는 같은 패키지 내에서만 접근이 가능, protected는 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능, public은 접근 권한이 전혀 없다.

 

캡슐화와 접근 제어자

접근 제어자를 그럼 왜 사용할까? 접근하지 못하게 한다고 했었다. 그렇게 되면서 클래스의 내부에 데이터를 보호하고 외부로부터 함부로 변경할 수 없게 하기 위함이다. 이거를 객체지향의 개념 중 캡슐화라고 한다. 코드로써 간단한 예제를 통해 알아보면 다음과 같다.

 

public class Infomation {

    String name = "최승대";
    String userId = "csd";
    String password = "1234****";
    String phoneNumber = "0107184****";

    public String getName() {
        return name;
    }

    public String getUserId() {
        return userId;
    }

    public String getPassword() {
        return password;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
}

이런 식의 정보 클래스가 있다고 가정하자. 여기서 get 메소드와 set 메소드라고 지칭했는데 이는 차후에도 대부분 이런 식으로 외부의 요소를 내보내거나 수정할 때 사용하기에 기억하자. 현재 get, set 메소드는 public으로 되어 있어 외부에서 접근이 가능한 상태이다. 실제로는 정책에 따라 다르겠지만 현재 상황은 어떤 정보도 외부로 보여지면 안된다는 것으로 가정하겠다. 멤버변수에도 값을 초기화 해두었다.

 

public class Hack {

    public static void main(String[] args) {
        Infomation yourInfo = new Infomation();
        System.out.println(yourInfo.getName());

        System.out.println(yourInfo.getUserId());
        System.out.println(yourInfo.getPassword());

        yourInfo.setName("최승소");
        System.out.println(yourInfo.getName());
    }
}

Hack이라고 하는 클래스를 만들어 해커의 집단이라고 가정해보았다. 현재 모두 public으로 어떤 패키지에서도 접근이 가능하기 때문에 캡슐화가 전혀 되지 않는 상황이다. 외부 클래스에서 해당 클래스에 접근이 가능하기에 멋대로 정보를 탈취하고 바꿔버릴 수도 있는 것이다.

 

보면 결과가 이렇게 되는 것을 알 수 있다. 현재 패키지 순서를 코드에 적어두질 않았는데 Hack 클래스와 Infomation 클래스는 다른 패키지에 있다. 그거와 별개로 private 접근 제어자를 사용해 수정해보겠다.

 

public class Infomation {

    String name = "최승대";
    String userId = "csd";
    String password = "1234****";
    String phoneNumber = "0107184****";

    private String getName() {
        return name;
    }

    private String getUserId() {
        return userId;
    }

    private String getPassword() {
        return password;
    }

    private String getPhoneNumber() {
        return phoneNumber;
    }

    private void setName(String name) {
        this.name = name;
    }

    private void setUserId(String userId) {
        this.userId = userId;
    }

    private void setPassword(String password) {
        this.password = password;
    }

    private void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
}

정보 클래스를 변경했다. Hack 클래스를 확인해보자.

 

접근이 불가능해지면서 외부의 클래스가 접근하지 못하게 되었다. 이것을 접근 제어자에 의한 캡슐화라고 볼 수 있다.

 

다형성

객체지향의 두번째 개념인 다형성에 대해 알아보겠다. 다형성은 여러 가지 형태를 가질 수 있는 능력을 말한다. 우리가 그동안 인스턴스를 생성하고 인스턴스 변수, 인스턴스 메소드를 어떻게 활용했었는지 학습했다.  위에서도 Infomation 클래스를 yourInfo 참조변수에 인스턴스 주소값을 넣고 생성하여 멤버를 사용해왔다. 

 

public class Tv {
    //...
    int x;

    public int getX(int x) {
        return x;
    }
}
public class SmartTv extends Tv {
    //...
    int y;
    int z;

    public int sum(int y, int z) {
        return y + z;
    }

    public int getX() {
        return super.x;
    }

    public int getY(int y) {
        return y;
    }

    public int getZ(int z) {
        return z;
    }
}
public class Test {
    public static void main(String[] args) {
        Tv s = new SmartTv();
    }
}

SmartTv 클래스의 인스턴스를 생성하는데 기존과는 다르게 무언가 이상하다. SmartTv 타입이 아니고 Tv 타입이다. 이렇게 하면 참조변수 s는 SmartTv 클래스의 인스턴스를 제대로 사용할 수 있을까?

 

그렇지 않다. 저 경우에는 SmartTv 클래스가 Tv 클래스를 상속받고 있는데 Tv 클래스에 정의되지 않은 sum, getX, getY, getZ 메소드는 사용할 수 없다. 물론 해당 이미지에서 보이는 getX는 Tv 클래스에 있는 메소드이다. 정리하자면, 자식 클래스의 인스턴스를 생성하는데 부모 클래스 타입으로 인스턴스를 생성할 수 있고 해당 인스턴스는 부모 클래스로부터 받아오는 멤버만 사용할 수 있다.

 

여기서 하나 의문점이 들 수도 있다. 그러면 반대로 자식 클래스 타입으로 부모 클래스의 인스턴스를 생성하는 것은 가능할까? 그것은 가능하지 않다.

 

참조변수의 형변환

이를 테면 정수형 변수를 문자열의 형태로 변환할 수 있다. 참조변수 또한 형변환이 가능하다. 다만, 아무렇게나 가능한 것은 아니고 자식 클래스와 부모 클래스 사이의 상속 관계가 있어야 가능하다.

 

class One { }
class Two extends One { }
class Three extends One { }

이런 식으로 클래스가 있다고 가정해보겠다. Two 클래스의 인스턴스를 생성했는데 One 타입으로 형변환을 했다. 가능할까? 가능하다. Two 클래스는 One 클래스를 상속받고 있기 때문이다. Two 클래스는 Three 클래스의 타입으로 형변환이 가능할까? 그것은 불가능하다. 참조변수의 형변환은 변수에 저장된 값인 주소값도 변하지 않는데 어디에 사용할까? 별 다를 건 없고 사용할 수 있는 멤버 개수를 조정하기 위함 정도로 알면 된다.

 

내일 할 부분

instanceof 연산자, 매개변수에서도 다형성이 존재하는 것 등등에 대해 학습하겠다.