• 카테고리

    질문 & 답변
  • 세부 분야

    프로그래밍 언어

  • 해결 여부

    해결됨

protected 접근 제어자 간단한 질문 및 코드 작성 방식?

23.02.23 05:17 작성 조회수 503

1

안녕하세요, 다름이 아니라 제가 퀴즈 #7, 퀴즈 #8, 섹션 8. 접근 제어자 (전반전 및 후반전) 강의를 듣고, 궁금한 것이 생겨서 이렇게 글을 남깁니다.

(1) 접근 제어자랑 관련된 간단한 질문입니다. 제가 올바르게 이해하고 있는지 한 번 확인해보고 싶습니다. 제가 강의를 들었을 땐, protected 예약어는 같은 패키지에서도 사용 가능하고, 다른 패키지에서 사용하고 싶을 땐, 자식 클래스에서 써야하는 걸로 이해했는데, 이 '같은 패키지'라는 말은 패키지 뿐만 아니라 (아마 당연하겠지만) '같은 클래스 안에서도 사용 가능하다'는 것도 내포하는 건가요?

(2) 코드 작성 방식에 관한 질문입니다. 퀴즈 #7 강의를 듣기 이전에 제 스스로 코드를 아래와 같이 적고 결과도 강의에서 제시한 바와 같이 똑같이 만들었습니다:

퀴즈 07 질문 1.jpg

그런데 선생님의 코드를 보니 한 클래스 내에서 여러 개의 생성자를 만들 수 있다는 것을 이용해서 아래 사진처럼 생성자 2개를 하나는 this()로, 하나는 String name 전달 값을 포함하는 public 생성자를 만들어서 이름을 지으셨는데, 혹시 이렇게 코드를 적는 방식의 차이가 있을까요? 또한 이름을 지을 때 그대로 this,name = "햄버거"; 또는 super,name = "치즈버거"; 이렇게 작성해도 상관없는 건가요? (중간에 반점은 점(.)을 넣게되면 글 안에서 하이퍼링크가 자동 생성되어 불가피하게 반점을 넣었습니다. 양해 부탁바랍니다.)

퀴즈 07 질문 2.jpg

3. 마찬가지로 코드 작성 방식에 관한 질문입니다. 퀴즈 #8 강의를 듣기 이전에 제 스스로 코드를 적고, 결과도 강의에서 제시한 바와 같이 똑같이 만들었습니다:

퀴즈 08 질문 1.jpg

다만 한 가지 다른 것은 저는 사진처럼 this.AccidentDetector = AccidentDetector;로 직접적으로 접근하는 방향으로 해서 코드를 썼고, 이 이후에는 this.detector.detect();나 this.reporter.report();를 이용한 것이 아닌 AccidentDetector.detect();VideoReporter.report();로 작성하여 코드를 완성했습니다. (비록 사진에는 나와있지 않지만...ㅎ) 이러한 경우에도 상관 없는 건가요?

지난 번에 제 글에 달린 답변은 정말로 자세해서 많이 도움이 되었습니다. 항상 감사합니다.

답변 1

답변을 작성해보세요.

1

안녕하세요?

(1) 네, protected 접근 제어자를 사용한다면 같은 패키지 내에서 접근이 가능합니다. '같은 클래스 안에서도 사용 가능하다' 는 것도 내포합니다.

(2) HamBurger 클래스는 CheeseBurger 와 ShrimpBurger 클래스가 상속하는 부모 클래스입니다. 그래서 자식 클래스에서는 super("버거 이름") 를 이용하여 부모 클래스에 정의된 생성자인 HamBurger(String name) 을 이용할 수 있지요.

public HamBurger(String name) {
    this.name = name;
}

이렇게 함으로써 main() 메소드에서는 편하게 아래와 같은 방법으로 그냥 기본 생성자를 호출하도록 하면 되었습니다.

hamBurgers[1] = new CheeseBurger();
hamBurgers[2] = new ShrimpBurger();

하지만 HamBurger 클래스도 동일하게 호출한다면 어떻게 될까요?

hamBurgers[0] = new HamBurger();

HamBurger 클래스는 별도로 상속하는 클래스가 없으므로 super("버거 이름") 을 이용할 수 없으며, 결국 생성과 동시에 이름을 정의하는 HamBurger(String name) 를 직접 호출해야 합니다. 만약 아래와 같이 main() 메소드에서 햄버거를 만드려고 시도한다면 this(); 가 없어도 되는데요.

hamBurgers[0] = new HamBurger("햄버거");

그렇게 하게 되면 다른 햄버거와 만드는 방식이 조금 달라집니다. 사용법이 달라지면 혼란을 유발할 수 있게 되겠죠.

HamBurger[] hamBurgers = new HamBurger[3];
hamBurgers[0] = new HamBurger("햄버거"); // 나 혼자 이름 전달 ㅠ_ㅠ
hamBurgers[1] = new CheeseBurger();
hamBurgers[2] = new ShrimpBurger();

그래서 new HamBurger() 를 통해 호출되는 기본 생성자를 추가로 정의하고 그 안에서 이름을 전달받는 사용자 정의 생성자를 this("햄버거") 를 통해 호출해주도록 한 것입니다.

public HamBurger() {
    this("햄버거"); // 사용자 정의 생성자 호출
}

그리고 부모 클래스에 정의된 인스턴스 변수를 접근하기 위해 super,name 또는 this,name 을 모두 사용할 수 있습니다.

아래는 간단한 예시입니다.

public class SampleClass {
    public static void main(String[] args) {
        CheeseBurger burger = new CheeseBurger();
        burger.printName();
    }
}

class HamBurger {
    public String name;

    public HamBurger() {
        this("햄버거");
    }

    public HamBurger(String name) {
        this.name = name;
    }
}

class CheeseBurger extends HamBurger {
    public CheeseBurger() {
        super("치즈버거");
    }

    public void printName() {
        System.out.println(this.name);
        System.out.println(super.name);
    }
}

실행 결과는 이렇구요.

치즈버거
치즈버거

단, CheeseBurger 내에 부모 클래스에 정의된 것과 동일한 인스턴스 변수를 새로 정의한다면 this,name 은 부모 클래스가 아닌 CheeseBurger 클래스에 정의된 name 변수에 접근하게 된다는 점을 주의하셔야 합니다. 확인을 위해 아래와 같이 코드를 수정하였습니다.

class CheeseBurger extends HamBurger {
    public String name = "가짜 치즈 버거"; // name 변수 추가

    public CheeseBurger() {
        super("치즈버거");
    }

    public void printName() {
        System.out.println(this.name);
        System.out.println(super.name);
    }
}

실행 결과는 이렇게 달라지게 되죠.

가짜 치즈 버거
치즈버거

(3) 작성하신 코드의 일부를 가져왔습니다.

private Detectable AccidentDetector;
public void setDetector(Detectable AccidentDetector) {
    this.AccidentDetector = AccidentDetector;
}

이렇게 작성하신 이후에 detect() 메소드에서는 this.AccidentDetector.detect(); 가 아닌 this 를 제외한 AccidentDetector.detect(); 로 하셨다는 말씀이시죠?

이 경우에는 this 가 있으나 없으나 모두 똑같이 SpeedCam 클래스의 AccidentDetector 에 접근하게 됩니다. 단, 클래스의 인스턴스 변수의 이름과 동일한 이름으로 메소드의 전달값 이름을 지정한다면 이때는 this 를 반드시 사용해야 합니다.

public void setDetector(Detectable AccidentDetector) {
    this.AccidentDetector = AccidentDetector;
}

위 예제에서 this. 을 붙인 AccidentDetector 는 SpeedCam 의 인스턴스 변수이며, this. 을 붙이지 않은 AccidentDetector 는 전달값으로 받은 AccidentDetector 가 됩니다. 그래서 아래와 같이 코드를 작성하게 되면 안됩니다.

public void setDetector(Detectable AccidentDetector) {
    AccidentDetector = AccidentDetector;
}

하지만 전달값에 정의된 이름이 인스턴스 변수와 같지 않다면 this. 이 없어도 괜찮습니다.

public void setDetector(Detectable detector) {
    AccidentDetector = detector;
}

구체적인 질문을 통해 점점 더 명확하게 지식을 쌓아가시는 열정이 존경스럽습니다 😊

감사합니다.

David님의 프로필

David

질문자

2023.02.27

네, 답변 감사합니다. 선생님 ㅎㅎ 덕분에 많은 도움이 되었습니다.

한 가지 더 여쭤보고 싶은 부분이 있는데요...ㅎ this랑 super에 대한 질문의 답이 이해가 잘 안되서요...ㅎ

(1) 2번 질문에 대한 답으로 이미 부모 클래스에서 정의된 인스턴스 변수를 접근하기 ㅜ이해서 super,name이나 this,name 모두 사용할 수 있다고 답을 받았고, 예시도 확인해서 2개 출력문 다 치즈버거로 잘 출력했습니다:

image

이는 이미 CheeseBurger 클래스가 'class CheeseBurger extends Hamburger' 이기 때문에, 즉, HamBurger 클래스를 상속 받기 때문에, this,name이나 super,name 2개의 변수 모두 같은 "치즈버거"를 가리키기 때문에 같은 결과가 출력되는 건가요?

비록 HamBurger 클래스에서는 this("햄버거"); 라고 했지만, 어차피 super,name에서 "치즈버거"라고 하면, 부모 이름인 "햄버거"에는 영향이 가지 않고, 자식 클래스에서만 이름의 변경사항이 반영되서, 최종적으로는 this,name이나 super,name이 "치즈버거"로 같이 변경되는 걸로 이해하는데 혹시 제가 올바르게 이해했을까요....? ...ㅎ

 

image

(2) 선생님께서 주신 또 다른 예시인 "가짜 치즈 버거", "치즈버거" 출력 예시에서는, this,name을 출력할 때는 이미 public String name = "가짜 치즈 버거";로 선언 한 것 때문에 가리키는 대상이 "가짜 치즈 버거"인 반면에, super,name은 (1)번과 마찬가지로 extends를 통한 상속 관계 (super("치즈버거");)때문에 "치즈버거"를 가리키는 거라 결과가 다르게 출력되는 걸로 이해하는데 이것도 혹시 제가 맞게 이해했을까요...? 혹시 몰라 제 나름대로 수학처럼(?) 한 번 적어보기도 했습니다...ㅎ 항상 좋은 답변 감사합니다 :-)

image

안녕하세요?

(1) 상속은 부모 클래스 기능의 확장이라고 보시면 됩니다. 그래서 부모 클래스의 모든 것을 사용하고 거기에 더해서 자식 클래스의 기능도 추가되는 것인데요. 부모 클래스의 name 변수도 내 것인양 사용할 수 있으므로 this,name 이 가능합니다. 현재 CheeseBurger 에는 name 변수가 따로 선언되어 있지 않고 부모 클래스의 name 변수만 사용하는 것이기 때문에 이는 부모 클래스의 name 에 접근하게 되는 거구요. super,name 은 부모 클래스의 name 변수임을 명시하는 것이기 때문에 동일하게 HamBurger 클래스의 name 변수에 접근하게 되는 겁니다.
CheeseBurger 객체를 만들 때 HamBurger 클래스의 기본 생성자는 호출되지 않습니다. 그러므로 this("햄버거"); 는 실행되지 않으며 사용자 정의 생성자인 HamBurger(String name) 이 호출됩니다. 이는 CheeseBurger 의 생성자에서 super(); 가 아닌 super("치즈버거"); 라고 적어서 그런 것입니다. 전달값이 없으면 기본 생성자를, 그렇지 않다면 전달값의 종류 및 개수에 해당하는 생성자가 호출됩니다. 그리고 super("치즈버거"); 라고 하게 되면 부모 클래스에 있는 name 변수의 값이 '치즈버거'로 설정됩니다. this,name 이나 super,name 이나 모두 부모 클래스의 name 변수를 뜻하므로 둘은 같은 결과를 출력하게 됩니다.

(2) 네, 맞습니다.
Case (1) 에서 CheeseBurger 에는 name 변수를 정의하지 않았으므로 this,name 이나 super,name 이나 모두 HamBurger 클래스의 name 변수에 설정한 '치즈버거'가 됩니다.
Case (2) 에서 CheeseBurger 에 name 변수를 새롭게 정의하였으므로 this,name 을 했을 때는 CheeseBurger 내에 있는 name 변수 값인 '가짜 치즈 버거'가 되며 super,name 을 했을 때는 HamBurger 내에 있는 name 변수 값인 '치즈버거' 가 됩니다.

잘 전달이 되었을까요? 😊
감사합니다.