어서와 Java는 처음이지! 제7장 상속
상속이란? 상속의 개념은 현실 세계에도 존재한다.
상속의 장점 상속의 장점 상속을 통하여 기존 클래스의 필드와 메소드를 재사용 기존 클래스의 일부 변경도 가능 상속을 이용하게 되면 복잡한 GUI 프로그램을 순식간에 작성 상속은 이미 작성된 검증된 소프트웨어를 재사용 신뢰성 있는 소프트웨어를 손쉽게 개발, 유지 보수 코드의 중복을 줄일 수 있다.
상속
부모 클래스와 자식 클래스 부모 클래스는 추상적이고 자식 클래스는 구체적이다.
상속의 형식
상속 예제: 자동차
자식 클래스는 부모 클래스를 포함
상속받은 필드와 메소드 사용 멤버 연산자(.)를 사용한다.
상속이 필요한 이유 코드를 재사용할 수 있다. 중복을 줄일 수 있다.
상속이 중복을 줄이는 이유
LAB: 동물 예제
SOLUTION: 동물 예제
LAB: 도형 예제 x와 y는 도형의 좌측 상단 좌표이다. width와 height는 사각형의 가로 길이와 세로 길이이다. print()에서는 도형의 위치를 화면에 출력한다. draw()에서는 사각형의 위치와 크기를 화면에 출력한다. area()에서는 사각형의 면적을 계산하여 반환한다.
SOLUTION: 도형 예제
SOLUTION: 도형 예제
SOLUTION: 도형 예제
상속과 접근제어
접근 제어 지정자
예제 public class Shape { private int x; private int y; void print() { System.out .println("x좌표: " + x + " y좌표: " + y); }
public class Rectangle extends Shape { private int width; private int height; double calcArea() { return width * height; } void draw() { System.out .println("(" + x + "," + y + ") 위치에 " + "가로: " + width + " 세로: " + height); 부모 클래스의 private 멤버 x와 y는 사용할 수 없다.
LAB: 직원과 매니저 클래스 직원(Employee)과 매니저(Manager)의 예
SOLUTION
SOLUTION
SOLUTION
예제 class Manager extends Employee { private int bonus; public void printSalary() { System.out.println(name + "(" + address + "):" + (salary + bonus)); } public void printRRN() { System.out.println(RRN); public class ManagerTest { public static void main(String[] args) { Manager m = new Manager(); m.printRRN(); 오류
예제
메소드 오버라이딩 메소드 오버라이딩(method overriding): 자식 클래스가 필요에 따라 상속된 메소드를 다시 정의하는 것
메소드 재정의의 예 Animal class Animal { public void sound() { } }; class Dog extends Animal { public void sound() { System.out.println("멍멍!"); } }; public class DogTest { public static void main(String[] args) { Dog d = new Dog(); d.sound(); Animal 1부터 10까지의 정수의 합 = 55 멍멍!
메소드를 오버라이딩하려면 메소드의 이름, 반환형, 매개 변수의 개수와 데이터 타입이 일치하여야 한다. 오버라이딩이 아님! public class Animal { public void sound() { } }; 오버라이딩이 아님! public class Dog extends Animal { public int sound() { } };
어노테이션 오버라이딩이 아님 public class Animal { public void sound() { } }; class Dog extends Animal { @Override void saund() { // 오류 발생! System.out.println("멍멍!"); } 1부터 10까지의 정수의 합 = 55 The method saund() of type Dog must override or implement a supertype method
키워드 super를 사용하여 부모 클래스 멤버 접근
LAB: 은행 클래스 작성하기
SOLUTION
SOLUTION
상속과 생성자 서브 클래스의 객체가 생성될 때, 서브 클래스의 생성자만 호출될까? 아니면 수퍼 클래스의 생성자도 호출되는가? class Base{ public Base(String msg) { System.out .println("Base() 생성자"); } }; class Derived extends Base { public Derived() { System.out .println("Derived() 생성자"); } }; public class Test { public static void main(String[] args) { Derived r = new Derived(); } };
실행 출력 생성자의 실행 순서는 (부모 클래스의 생성자) -> (자식 클래스의 생성자) 순으로 된다.
명시적인 생성자 호출 super를 이용하여서 명시적으로 수퍼 클래스의 생성자 호출 class Shape { public Shape(String msg) { System.out.println("Shape 생성자() " + msg); } }; public class Rectangle extends Shape { public Rectangle(){ super("from Rectangle"); // 명시적인 호출 System.out.println("Rectangle 생성자()"); } };
묵시적인 생성자 호출 //super() 호출 class Shape { public Shape( ) { System.out.println("Shape 생성자()"); } }; class Rectangle extends Shape { public Rectangle() { System.out.println("Rectangle 생성자()"); } }; //super() 호출 1부터 10까지의 정수의 합 = 55 Shape 생성자 Rectangle 생성자
LAB: 복잡한 상속 계층 구조 만들어 보기 예를 들어서 Shape이 Rectangle의 부모 클래스이고 다시 Rectangle이 ColoredRectangle의 부모 클래스가 될 수 있는 것이다. 이런 경우의 ColoredRectangle은 Shape와 Rectangle의 모든 멤버를 상속받게 된다.
SOLUTION public class Shape { private int x; private int y; public Shape(int x, int y) { System.out .println("Shape()"); this.x = x; this.y = y; }
SOLUTION public class Rectangle extends Shape { private int width; private int height; public Rectangle(int x, int y, int width, int height) { super(x, y); System.out .println("Rectangle()"); this.width = width; this.height = height; } double calcArea() { return width * height;
SOLUTION public class ColoredRectangle extends Rectangle { String color; public ColoredRectangle(int x, int y, int width, int height, String color) { super(x, y, width, height); System.out .println("ColoredRectangle()"); this.color = color; } public static void main(String[] args) { ColoredRectangle obj = new ColoredRectangle(10, 10, 20, 20, "red");
추상 클래스 추상 클래스(abstract class): 몸체가 구현되지 않은 메소드를 가지고 있는 클래스 추상 클래스는 추상적인 개념을 표현하는데 적당하다.
추상 클래스의 정의 public abstract class Animal { public abtract void move(); ... }; 추상 메소드 정의
추상 메소드의 예
abstract class Shape { private int x, y; public void move(int x, int y) this.x = x; this.y = y; } public abstract void draw(); }; class Rectangle extends Shape { private int width, height; public void draw() { // 추상 메소드 구현 System.out .println("사각형 그리기 메소드"); class Circle extends Shape { private int radius; public void draw() { System.out .println("원 그리기 메소드");
다형성이란? 다형성(polymorphism)이란 객체들의 타입이 다르면 똑같은 메시지가 전달되더라도 서로 다른 동작을 하는 것
자바에서의 자료형 검사 클래스 A의 참조 변수로 클래스 B의 객체를 참조할 수는 없다. class A { A() { } } class B { B() { } public class TypeTest1 { public static void main(String args[]) { A a = new B(); // NO!
상향 형변환 부모 클래스의 참조 변수는 자식 클래스의 객체를 참조할 수 있다! class A { A() { } } class B extends A { B() { } public class TypeTest1 { public static void main(String args[]) { A a = new B(); // OK! }
상속과 다형성 하나의 예로 Rectangle, Triangle, Circle등의 도형 클래스가 부모 클래스인 Shape 클래스로부터 상속되었다고 가정하자. e
상향 형변환 class Shape { protected int x, y; } class Rectangle extends Shape { private int width, height; class Triangle extends Shape { private int base, height; class Circle extends Shape { private int radius;
public class ShapeTest { public static void main(String arg[]) { Shape s1, s2; s1 = new Shape(); // ① 당연하다. s2 = new Rectangle(); // ② Rectangle 객체를 Shape 변수로 // 가리킬 수 있을까? }
왜 그럴까? 서브 클래스 객체는 수퍼 클래스 객체를 포함하고 있기 때문이다.
예제 .. public class ShapeTest { public static void main(String arg[]) { Shape s= new Rectangle(); Rectangle r = new Rectangle(); s.x = 0; s.y = 0; s.width = 100; s.height = 100; } 1부터 10까지의 정수의 합 = 55 컴파일 오류가 발생한다. s를 통해서는 Rectangle 클래스의 필드와 메소드에 접근할 수 없다. width cannot be resolved or is not a field height cannot be resolved or is not a field
다형성
형변환 Shape s = new Rectangle(); s를 통하여 Rectangle 클래스의 필드와 메소드를 사용하고자 할 때는 어떻게 하여야 하는가? ((Rectangle) s).setWidth(100);
하향 형변환 서브 클래스 참조 변수로 수퍼 클래스 객체를 참조하는 것으로 일반적인 상황에서는 컴파일 오류이다. 만약 서브 클래스 객체인데 형변환에 의하여 일시적으로 수퍼 클래스 참조 변수에 의하여 참조되고 있는 경우는 가능 Rectangle r; r = new Shape(); // NOT OK! Rectangle r; Shape s; s = new Rectangle(); r = (Rectangle)s; r.width = 100; // public 이면 ok r.height = 100;
동적 메소드 호출 다형성은 객체들이 동일한 메시지를 받더라도 각 객체의 타입에 따라서 서로 다른 동작을 하는 것
class Shape { protected int x, y; public void draw() { System.out.println("Shape Draw"); } class Rectangle extends Shape { private int width, height; System.out.println("Rectangle Draw");
class Triangle extends Shape { private int base, height; public void draw() { System.out.println("Triangle Draw"); } class Circle extends Shape { private int radius; System.out.println("Circle Draw");
public class ShapeTest { public static void main(String arg[]) { Shape s1, s2, s3, s4; s1 = new Shape(); s2 = new Rectangle(); s3 = new Triangle(); s4 = new Circle(); s1.draw(); s2.draw(); s3.draw(); s4.draw(); }
실행 결과 Shape Draw Rectangle Draw Triangle Draw Circle Draw
동적 바인딩 메소드 호출을 실제 메소드의 몸체와 연결하는 것을 바인딩(binding)이라고 한다. 자바 가상 머신(JVM)은 실행 단계에서 객체의 타입을 보고 적절한 메소드를 호출하게 된다. 이것을 동적 바인딩(dynamic binding) 또는 가상 메소드 호출(virtual method invocation)이라고 한다.
예제 public class ShapeTest { private static Shape arrayOfShapes[]; public static void main(String arg[]) { init(); drawAll(); } public static void init() { arrayOfShapes = new Shape[3]; arrayOfShapes[0] = new Rectangle(); arrayOfShapes[1] = new Triangle(); arrayOfShapes[2] = new Circle(); public static void drawAll() { for (int i = 0; i < arrayOfShapes.length; i++) { arrayOfShapes[i].draw();
예제 Rectangle Draw Triangle Draw Circle Draw
어떤 장점이 있을까? 위와 같은 새로운 클래스가 추가되더라도 다른 코드는 변경할 필요가 없다. class Cylinder extends Shape { private int radius, height; public void draw(){ System.out.println("Cylinder Draw"); } 위와 같은 새로운 클래스가 추가되더라도 다른 코드는 변경할 필요가 없다.
메소드의 매개 변수 메소드의 매개 변수로 부모 클래스 참조 변수를 이용한다. -> 다형성을 이용하는 전형적인 방법
예제 public class ShapeTest { public static void printLocation(Shape s) { System.out.println("x=" + s.x + " y=" + s.y); } public static void main(String arg[]) { Rectangle s1 = new Rectangle(); Triangle s2 = new Triangle(); Circle s3 = new Circle(); printLocation(s1); printLocation(s2); printLocation(s3);
LAB: 동적 메소드 호출 강아지와 고양이를 나타내는 클래스를 작성하자. 이들 클래스의 부모 클래스로 Animal 클래스를 정의한다. 강아지와 고양이 클래스의 sound() 메소드를 호출하면 각 동물들의 소리가 출력되도록 프로그램을 작성해보자. Animal 클래스의 sound() 멍멍 야옹
SOLUTION class Animal { void sound() { System.out.println("Animal 클래스의 sound()"); } class Dog extends Animal { System.out.println("멍멍"); class Cat extends Animal { System.out.println("야옹");
SOLUTION public class DynamicCallTest { public static void main(String args[]) { Animal animal = new Animal(); Dog dog = new Dog(); Cat cat = new Cat(); Animal obj; obj = animal; obj.sound(); obj = dog; obj = cat; }
Object 클래스 Object 클래스는 java.lang 패키지에 들어 있으며 자바 클래스 계층 구조에서 맨 위에 위치하는 클래스
Object의 메소드 C
getClass() obj is of type Car obj is of type Car class Car { ... } public class CarTest { public static void main(String[] args) { Car obj = new Car(); System.out.println("obj is of type " + obj.getClass().getName()); obj is of type Car obj is of type Car
equals() 메소드 Object에서 제공되는 equals()는 == 연산자를 사용하여서 객체의 주소가 동일한지를 검사하여서 true 또는 false를 반환한다. 하지만 객체에 대해서는 이것이 올바르지 않는 경우가 많이 있다. String s1= new String(“abcdef”); String s2= new String(“abcdef”); String 클래스의 equals 메소드는 주소 대신에 동일한 문자열을 검사!
equals() 메소드 Car 클래스의 equals 메소드 재정의 예 class Car { private String model; public Car(String model) { this.model= model; } public boolean equals(Object obj) { if (obj instanceof Car) return model.equals(((Car) obj).model); else return false; } public class CarTest { public static void main(String[] args) { Car firstCar = new Car("HMW520"); Car secondCar = new Car("HMW520"); if (firstCar.equals(secondCar)) { System.out.println("동일한 종류의 자동차입니다."); } else { System.out.println("동일한 종류의 자동차가 아닙니다."); Object의 equals()를 재정의 1부터 10까지의 정수의 합 = 55 동일한 종류의 자동차입니다.
객체의 실제타입 instanceof 연산자 Shape s = getShape(); if (s instanceof Rectangle) { System.out.println("Rectangle이 생성되었습니다"); } else { System.out.println("Rectangle이 아닌 다른 객체가 생성되었습니다"); }
hashCode() 메소드 hashCode()는 해싱이라는 탐색 알고리즘에서 필요한 해시값을 생성하는 메소드이다. // 현 객체에 대한 해시 코드 반환
Object의 toString()를 재정의 public String toString() { return getClass().getName()+”@”+Integer.toHexString(hashCode()); } public class Car { private String model; public Car(String model) { this.model = model; } public String toString() { return “모델: “ + model; Object의 toString()를 재정의
IS-A 관계 is-a 관계: “~은 ~이다”와 같은 관계 상속은 is-a 관계이다. 자동차는 탈것이다(Car is a Vehicle). 강아지는 동물이다(Dog is a animal).
HAS-A 관계 has-a 관계: ~은 ~을 가지고 있다”와 같은 관계 도서관은 책을 가지고 있다(Library has a book). 거실은 소파를 가지고 있다(Living room has a sofa).
HAS-A 관계 객체 지향 프로그래밍에서 has-a 관계는 구성 관계(composition) 또는 집합 관계(aggregation)를 나타낸다.
HAS-A 관계의 구현 class Vehicle { } class Carburetor { } public class Car extends Vehicle{ private Carburetor cb; }
HAS-A 관계의 예제
public class Date { private int year; private int month; private int date; public Date(int year, int month, int date) { this.year = year; this.month = month; this.date = date; } @Override public String toString() { return "Date [year=" + year + ", month=" + month + ", date=" + date + "]";
public class Employee { private String name; private Date birthDate; public Employee(String name, Date birthDate) { this.name = name; this.birthDate = birthDate; } @Override public String toString() { return "Employee [name=" + name + ", birthDate=" + birthDate + "]";
public class EmployeeTest { public static void main(String[] args) { Date birth = new Date(1990, 1, 1); Employee employee = new Employee("홍길동", birth); System.out.println(employee); } Employee [name=홍길동, birthDate=Date [year=1990, month=1, date=1]]
종단 클래스 종단 클래스(final class)는 상속을 시킬 수 없는 클래스를 말한다. 종단 클래스가 필요한 이유는 주로 보안상의 이유 때문이다. public final class MyFinal {...} public class ThisIsWrong extends MyFinal {...}
종단 메소드 특정한 메소드만 오버라이드될 수 없게 만들려면 종단 메소드(final method)로 선언 class Baduk { enum BadukPlayer { WHITE, BLACK } ... final BadukPlayer getFirstPlayer() { return BadukPlayer.BLACK; }
정적 메소드 오버라이딩 부모 클래스의 메소드 중에서 정적 메소드를 재정의(오버라이드)하면 부모 클래스 객체에서 호출되느냐 아니면 자식 클래스에서 호출되느냐에 따라서 호출되는 메소드가 달라진다. public class Animal { public static void eat() { System.out.println("Animal의 정적 메소드 eat()"); } public void sound() { System.out.println("Animal의 인스턴스 메소드 sound()");
정적 메소드 오버라이딩 public class Cat extends Animal { public static void eat() { System.out.println("Cat의 정적 메소드 eat()"); } public void sound() { System.out.println("Cat의 인스턴스 메소드 sound()"); public static void main(String[] args) { Cat myCat = new Cat(); Animal myAnimal = myCat; Animal.eat(); myAnimal.sound();
실행 결과 Animal의 정적 메소드 eat() Cat의 인스턴스 메소드 sound()