Week 4 of Object oriented programming - Inheritance

Week 4 of Object oriented programming - Inheritance

이번 주차 중 상속에 관한 내용을 먼저 리뷰한다.


상속을 하는 키워드는 extends임을 알고 진행한다.



Students, Faculty라는 2개의 클래스를 정의할 때 기존의 Person이라는 클래스와 유사하게 다룰수 있는지 방법을 생각해본다.


예상되는 답변 중 하나는 Person이라는 클래스 내에 Student와 Faculty의 속성을 정의할 수 있겠다.


하지만 Graduate, FullTime 등 다양한 객체가 등장할 때 굉장히 구조가 복잡해진다.


이를 스파게티코드라하고 프로그래밍에서는 굉장히 지양하는 구조이다.


다른 방법으로는 Student와 Faculty를 각각 Person과 동일하게 복사해올 수 있겠다.


하지만 위의 퀴즈에서 알 수 있는 것처럼 Person의 구조가 변경될 때마다 Student와 Faculty의 구조 또한 동일하게 바꿔줘야하는 문제가 생긴다.


말 그대로 일관성을 확보할 수 없는 문제이다


그렇다면 리스트 형식으로 변환해본다면


이또한 깔끔하게 정리할 수 없음을 알 수 있다.


그러므로 위의 3가지를 만족하는 방법을 찾아야 한다.


이전의 복사하는 방식을 생각해볼때


Student는 Person을 extends하여 속성을 동일하게 가져갈 수 있다. extends의 의미는 이후의 클래스로부터 상속받는다는 의미이다.


상위의 클래스를 base / super class라고 하며 하위 클래스를 derived / sub class라고 한다.


상속받을때에는 Public instance variables, Public method, Private instance variables를 받게된다.


이전에도 나온 주의점이지만 Private 변수는 public method인 setter를 통해서만 접근이 가능하다.


Student class는 Person class로부터 상속받았기에 위와 같이 화살표로 연결해 표현한다.


상속도를 그리게 되면 위와 같은 모습이 될 것이며


한번 Student class에는 속하고 Faculty class에는 속하지 않는 속성을 생각해보는 시간을 갖는다.


퀴즈로 등장하며 GPA같은 경우는 Student class에만 적용이 될 것이며 Salary같은 경우는 Faculty class에만 해당될 것이다.


그래서 하위에 GPA, salary 같은 변수와 getter method를 설정한 것을 확인할 수 있다.


그래서 name은 공통으로 사용되고 gpa나 salary는 개별으로 사용될 것이다.


그래서 1, 2번에 해당하는 점을 해결하였고 다음은 3번째 항목에 대해 어떻게 해결할 것인지 접근해본다.


여기서는 is-a라는 구문이 등장하는데 클래스간 관계를 파악하는데 유용하다. Person p = new Person(); 이 코드에서는 A Person 'is-a' Person이라고 변환이 되어 말이 되는지 여부를 확인할 수 있다.


마찬가지로 Student s = new Student(); 코드를 바꾸면 A Student 'is-a' Student이므로 말이 성립한다.


또한 Person p = new Student(); 코드는 A Student 'is-a' Person이므로 성립한다.


그러므로 Person 배열은 Student, Faculty 객체까지 저장할 수 있다.


하지만 Student s = new Person의 경우 A Person 'is-a' Student는 예외상황이 있을 수 있으므로 성립하지 않는다.


이제 배운내용을 되짚어 본다.


왼쪽 코드를 보고 오른쪽 코드 중 성립하지 않는, 에러가 발생하는 코드를 찾아내는 것이다.


위의 문제와 동일하게 코드가 작성되어 있다.


1, 2번째 코드는 성립하며


3번째는 p가 Person 클래스인데 여기에는 getID라는 메쏘드가 없으므로 에러가 발생한다. 만약 바꾼다면 위와 같이 (Student)p와 같이 타입을 변경하여 사용해야 한다.


4번째는 f는 Faculty이며 q는 Person인데 'is-a'를 대입해보면 A Person 'is-a' Faculty라는 구문은 성립하지 않으므로 에러이다.


다시금 되짚어주고 마무리하게 된다.


이번에는 Public, Private 타입에 대한 얘기를 한다. Public type은 어떤 클래스에서건 접근이 가능하다.


Private type은 같은 클래스 내에서만 접근이 가능하다.


중요한 원칙으로 멤버변수는 private로 선언하길 권장한다.


public과 private 사이에는 protected와 package 타입이 존재하는데 protected는 같은 클래스/패키지 내에서 접근이 가능하고 하위클래스에서도 접근이 가능하다.


package 타입은 같은 클래스나 같은 패키지에서만 접근이 가능하다.


도식도로 보면 더 쉽게 이해가 가능하다.


public w는 어디서나 접근이 가능하고


protected x 는 같은 클래스/패키지/하위클래스에서 접근이 가능하다.


package내에 있는 y는 같은 클래스/패키지에서 접근이 가능하며


private z는 같은 클래스내에서만 접근이 가능하다.


클래스를 설계할때 옳은 것으로 변수들을 private으로 선언하고 method는 public으로 선언하자는 말이다.


다시 강의로 돌아와서 상속관계를 위와 같이 표현할 수 있다고 했다.


new라는 함수는 공간을 할당하며


뒤의 클래스는 생성자를 통과하도록 한다.


객체는 항상 inside-out방식으로 생성이 된다.


Student의 경우 Person을 상속하고 Person은 자동적으로 Object를 상속한다.


따라서 가장 상위의 indirect super class가 Object 클래스가 되며


다음의 super class는 Person


마지막으로 sub class는 Student가 해당된다.


객체 생성 시 어떤 순서로 인스턴스 변수가 초기화 되는지 묻는데 위에서 본바와 같이 최상단의 클래스부터 하부 클래스에 이르는 way-down방식으로 진행된다.


이제 Compiler에 관한 이야기를 하는데 코드를 JAVA compiler를 거칠 때 새로운 명령들을 삽입한다.


그 후 Bytecode로 전환하게 된다.


그렇다면 위의 코드는 컴파일러가 어떻게 변환하는가?


위처럼 indirect super class를 상속하도록 입력해준다.


다음은 생성자가 없다면 추가해준다.


그리고 같은 클래스내에 생성자를 호출하는 문구나 상위클래스의 생성자를 호출하는 문구가 있어야 하기에 추가해주며 혹은 super();을 입력해준다.


그래서 위와 같은 내용까지 추가가 된다.


이번엔 Student class의 경우를 살펴본다. 먼저 Person 으로부터 상속받는 내용이 있기에 Object를 상속하는 내용을 추가하지 않는다.


생성자가 없으므로


Student 생성자를 만들고 super();을 기입해 default 생성자로 호출하게 해준다.


그리고 이전의 inside-out 방식이 진행된다.


하지만 이름을 어떻게 초기화 시키는지 알아야 한다.


컴파일 과정을 알도록하는 이유를 알도록 퀴즈가 등장한다.


그리고 변수 초기화를 진행하게 됨을 알려준다.


먼저 Person 클래스에서는 위와 같이 name 변수에 대한 내용을 담게되며


이때 유의할 점은 super메쏘드는 항상 최상단에 위치해야 한다고 한다.


그렇다면 위와 같은 모습을 유지한 체


다음 Student 클래스로 넘어가면


상위클래스로부터 name 변수를 끌어오게 된다.


Person 클래스의 private 형인 name이 Student 클래스에 똑같이 적용되는 원리이다.


그렇다면 어떻게 public setter없이 name을 초기화 할 수 있는지 본다.


그냥 위의 두줄을 삭제하고 super(n);을 입력하면 된다.


그리고 인자가 없는 생성자를 추가하는데


위와 같이 디폴트값인 이름을 사용해준다.


그럼 동일 클래스의 생성자를 사용하게된다.


간단한 퀴즈가 등장하고 this를 통해 상위 변수나 메쏘드에 접근해야될 때에는 super()메쏘드가 먼저 존재해야 한다.


다시 되짚어 보는 시간을 가져본다.


우선 위와 같은 내용의 코드를 주고


어떤 순서로 출력값이 나오는지 유추해본다.


객체는 inside-out 방식으로 생성하므로 Student s = new Student(); 를 수행한다면 Student 클래스로 가면 this로 인자가 있는 생성자로 이동하고 super를 통해 상위의 Person으로 접근한다. 그렇게 되면 Person 클래스 내의 #1이 먼저 출력이 되고 이후 거꾸로 빠져나오므로 #3, #2 순으로 출력된다.


다음 문제를 주고 예상되는 결과를 유추한다.


토론내용을 통해 답을 유추할 수 있었다.



Person 클래스를 보면 인자가 있는 생성자만 존재하는데 Student 클래스에서는 인자를 넘기지 않고 사용을 하였다. 따라서 컴파일 단계에서 Person클래스의 무인자 생성자를 호출하려 하지만 존재하지 않으므로 에러가 발생 할 것이다.


컴파일 단계에서 Student 클래스 내에 super();가 추가되는 것도 짚어준다.


이제 3가지 요소가 모두 충족된 상태이므로 다음 내용을 넘어간다.


Overloading, Overriding에 관한 내용이며 Overloading은 같은 클래스 내에서 같은 메쏘드가 파라메터들이 다른 상태로 여러개 존재한 것을 말하며 Overriding은 상위클래스의 메쏘드가 하위 클래스에 같은 이름의 메쏘드로 같은 파라메터를 가지고 있을 때를 칭한다.


toString이라는 메쏘드를 예로 들어 설명한다.


객체에서의 toString() 메쏘드를 Override하는 모습이다. 상속을 통해 동일하게 존재하게 될 것이며


위와 같은 코드로 기입하게 된다.


그래서 Person 클래스의 toString 메쏘드를 호출할 수 있겠고


참고적으로 위와 같이 입력해도 자동적으로 toString 메쏘드를 호출한다고 한다.


그럼 위와 같이 출력이 나오게 되고


이제 Student 클래스에서 상황을 본다.


목표는 Student ID를 출력하는 것이고


위와 같이 구성을 한다면 this이기에 해당 클래스를 참조하며


super는 상위 클래스를 참조한다.


다시 돌아와서 Student의 toString 메쏘드를 호출하고


해당 포맷에 맞춰 결과를 출력한다.


만약 Student 클래스가 아닌 Person 클래스라면


결과는 동일하게 나온다. 그 이유는 다형성(Polymorphism)으로 설명한다.


Overriding에 관해 사실인 것은 상위클래스와 하위클래스의 두 메쏘드가 같은 이름이고 같은 파라메터일 때이다.

댓글