https://github.com/whiteship/live-study/issues/6
목차
1. 자바 상속의 특징
2. super 키워드
3. 메소드 오버라이딩
4. 다이나믹 메소드 디스패치
5. 추상 클래스
6. final 키워드
7. Object 클래스
2. super 키워드
constructor
자신의 다른 생성자를 호출할때 this()를 사용하는데, 부모 클래스의 생성자를 사용하기 위해 super()를 사용할 수 있다. 그런데 모든 생성자는 첫줄에 super()를 call 해야한다. 포함시키지 않으면 컴파일러가 자동으로 기본생성자 super()를 넣는다.
public class Foo {
public Foo(int x) {
System.out.println("foo");
}
public class Goo extends Foo {
public Goo() {
System.out.println("goo");
)
위 코드에서 컴파일에러가 난다. Goo 생성자에서 super()를 자동적으로 call하는데 부모 클래스인 Foo에서 기본생성자가 없기 때문이다. 그래서 에러를 해결하기 위해서는 Foo에서 기본생성자를 만들거나, 혹은 Goo에서 super(int x) 를 호출하면 된다. 애초에 프로그래밍을 할때, default constructor를 작성하고 super()를 작성하는 습관을 들여놓는 것이 좋다.
참조변수
부모 클래스의 멤버에 접근하기 위해 참조변수 super를 사용할 수 있다.
class Employee {
/*내용생략*/
public String toString()
{
return getName() + " " + getHireDate()
}
}
class HourlyEmployee extends Employee {
/*내용생략*/
public String toString()
{
return super.toString() + wageRage + " " + hours;
}
}
위 예제와 같이 super를 이용해서 Employee에 접근해서 Employee의 toString을 사용할 수 있다.
3. 메소드 오버라이딩
Overriding
오버라이딩은 자손클래스에서 조상클래스의 메서드를 재정의하는 것을 의미한다.
1. 조상클래스의 메서드와 메서드 이름이 같아야하고
2. 파라미터의 갯수와 타입이 같아야하며
3. return 타입도 같아야한다. 그런데 return 타입의 경우 원래 return 타입의 자손클래스를 return타입으로 사용할 수 있다. 이를 covariant return type으로 부른다.
static method의 경우 자식 클래스에서 같은 이름의 static method를 정의할 수 있다. 하지만 이것은 오버라이딩하는 것은 아니고 조상의 static method를 숨기는 것이다.
class Animal {
public static void testClassMethod() {
System.out.println("The static method in Animal");
}
public void testInstanceMethod() {
System.out.println("The instance method in Animal");
}
}
public class Cat extends Animal {
@Override
public static void testClassMethod() {
System.out.println("The static method in Cat");
}
@Override
public void testInstanceMethod() {
System.out.println("The instance method in Cat");
}
public static void main(String[] args) {
Cat myCat = new Cat();
Animal myAnimal = myCat;
Animal.testClassMethod();
myAnimal.testClassMethod();
myAnimal.testInstanceMethod();
}
}
위 코드를 실행하면 결과로 The static method in Animal, The static method in Animal, The instance method in Cat 가 순서대로 출력된다. Cat 인스턴스를 Animal의 참조변수인 myAnimal로 사용하고 있는데 static method는 참조변수를 따라가고 instance method는 참조변수가 아닌 그 인스턴스를 따라 실행되는 것을 확인할 수 있다.
오버라이딩의 다른 조건
1. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
@Override 를 사용하면 오버라이딩을 하고 있는지 검증을 받을 수 있다.
parent의 doSomething()의 접근제어자가 protected인 경우 child 클래스의 doSomething() 클래스의 접근제어자는 protected 혹은 public이어야하고
parent의 doSomething()이 접근제어자가 public이라면 child의 doSomething() 또한 public이어야한다.
4. 다이나믹 메소드 디스패치
Static Method Dispatch
public class StaticDispatch {
public static void main(String[] args) {
Cat cat = new Cat();
cat.cry();
}
}
class Cat {
public void cry() {
System.out.println("Meow");
}
}
디스패치란 어떤 메서드를 호출할 것인지 결정하는 과정이다. 그 중 Static Method Dispatch는 컴파일타임에 어떤 메서드가 실행될 지 명확하게 알고 있는 것을 말한다. 메인 함수에서 cat.cry()를 호출했으니 클래스 cat의 cry를 사용하겠구나 미리 알 수 있는 것이다.
Dynamic Dispatch
public class DynamicDispatch {
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.cry();
dog.cry();
}
}
abstract class Animal {
abstract void cry();
}
class Cat extends Animal {
@Override
public void cry() {
System.out.println("Meow");
}
}
class Dog extends Animal {
@Override
public void cry() {
System.out.println("Woof");
}
}
다음 예제에서는 Animal이라는 추상클래스를 만들고 Cat과 Dog가 Animal의 cry를 구현하게 만들었다. 그리고 main 함수에서 참조변수 Animal에 Cat과 Dog 인스턴스를 담고 cry를 호출했다.
이 때는 static dispatch와는 다르게 컴파일 타임에 어떤 cry를 호출할지 알 수 없고 런타임에 결정하게 된다. 런타임에 메서드를 호출할 때, 어떤 객체인지 판단한 후 클래스의 메서드를 호출하는 것이다.
Double Dispatch
interface Post {
void postOn(SNS sns);
}
class Text implements Post {
public void postOn(SNS sns) {
sns.post(this);
}
}
class Picture implements Post {
public void postOn(SNS sns) {
sns.post(this);
}
}
interface SNS {
void post(Text text);
void post(Picture picture);
}
class Facebook implements SNS {
public void post(Text text) {
// text -> facebook
}
public void post(Picture picture) {
// picture -> facebook
}
}
class Twitter implements SNS {
public void post(Text text) {
// text -> twitter
}
public void post(Picture picture) {
// picture -> twitter
}
}
public static void main(String[] args) {
List<Post> posts = Arrays.asList(new Text(), new Picture());
List<SNS> sns = Arrays.asList(new Facebook(), new Twitter());
posts.forEach(p -> sns.forEach(s -> p.postOn(s)));
}
총 두번의 디스패치가 일어난다.
1. post 타입에 저장된 postOn에서 Text, Picture 중에서 어떤 구현체의 postOn을 사용할지
2. postOn에서 sns 중에 Facebook, Twitter 중에서 어떤 구현체의 post를 사용할지
이렇게 되면 Facebook, Twitter 뿐만 아니라, 밑의 Instagram과 같이 새로운 코드를 추가하는 것이 용이해진다.
class Instagram implements SNS {
public void post(Text text) {
// text -> instagram
}
public void post(Picture picture) {
// picture -> instagram
}
}
만약 더블디스패치를 사용하지 않는다면, postOn을 사용할 때, 어떤 SNS를 사용할지 체크해주는 다음과 같은 코드를 사용하게 될 것이고, Instagram을 추가할 때마다 Text와 Picture의 postOn을 수정해야하는 번거로움이 있을 것이다.
잘못된 예시
static class Text implements Post {
public void postOn(SNS sns) {
if (sns instanceof Facebook) {
System.out.println("facebook - text");
}
if (sns instanceof Twitter) {
System.out.println("twitter - text");
}
}
}
static class Picture implements Post {
public void postOn(SNS sns) {
if (sns instanceof Facebook) {
System.out.println("facebook - picture");
}
if (sns instanceof Twitter) {
System.out.println("twitter - picture");
}
}
}
출처 : 토비의 봄 TV1회 - 재사용성과 다이나믹 디스패치, 더블 디스패치
5. 추상 클래스
추상클래스는 키워드 abstract를 사용해서 선언할 수 있다. 추상클래스는 추상메서드를 포함한 클래스인데, 추상메서드는 구현부는 없고 선언부만 있는 클래스이다. 마찬가지로 abstract 제어자를 붙여야하고, 구현부가 없으므로 {}를 사용하지 않고 대신 세미콜론 ; 을 사용한다.
구현부가 없기 때문에 추상클래스는 인스턴스화할 수 없다. 대신 추상클래스를 상속해서 클래스를 만들어 사용할 수 있다. 추상클래스를 설계도로 사용해서 추상메서드를 구현하며 비교적 쉽게 클래스를 작성할 수 있다.
여러 관련된 클래스가 공유된 코드가 있거나, public이 아닌 다른 접근제어자를 사용하고 싶거나, non static, non final field를 사용하고 싶을 때 추상클래스를 사용하면 좋다.
abstract class GraphicObject {
/*내용 생략*/
abstract void draw();
abstract void resize();
}
class Circle extends GraphicObject {
void draw() {
/*내용 생략*/
}
void resize() {
/*내용 생략*/
}
}
class Rectangle extends GraphicObject {
void draw() {
/*내용 생략*/
}
void resize() {
/*내용 생략*/
}
}
예를 들어 Rectangle, Circle, Line 등 다양한 도형에 대한 클래스가 있다고 하자. 거기서 draw() resize() 등 공통적으로 사용하는 method가 있으면, 추상메서드를 만들어서 중복되는 코드를 효율적으로 관리할 수 있다.
6. final 키워드
제어자 | 대상 | 의미 |
final | 클래스 | 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다. 다른 클래스의 조상이 될 수 없다. |
메서드 | 변겨될 수 없는 메서드, 오버라이딩을 통해 재정의될 수 없다. | |
멤버변수 | 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다. | |
지역변수 |
7. Object 클래스
Object 클래스를 쓰는 이유
Object를 Fist-class citizen이라고 부르는데, 함수는 아니지만 parameter로 넘기거나 변수에 담거나 return 할 수 있어야 한다. 모든것을 Object로 취급하기 위해 class instance를 지칭하는 하나의 타입이 필요하기 때문에 최상위에 하나를 두는 것이다.
Reference
1. 자바의 정석 3판
'자바 > 백기선 자바스터디' 카테고리의 다른 글
[백기선 자바스터디] 인터페이스 (0) | 2022.08.10 |
---|---|
[백기선 자바스터디] 패키지 (0) | 2022.07.25 |
[백기선 자바스터디] 클래스 (0) | 2022.07.06 |
[백기선 자바스터디] 연산자 : instanceof, shift 연산자 (0) | 2022.07.02 |
[백기선 자바스터디] 자바 데이터 타입, 변수 : 형변환, 타입추론 (0) | 2022.06.25 |