[JAVA] TIL 012 - 23.07.27

유진·2023년 7월 27일

08_상속(Inheritance)

  • 상속(다형성의 베이스) = 부모객체(속성,기능 = 필드+메서드)를 자식객체에게 물려주는 것 -> 메모리구조 중요!
  • 자식 객체는 자신의 속성,기능 + 부모의 속성, 기능까지 사용할 수 있음

  • 상속의 장점
  1. 유지보수성 증가
  2. 재사용성 증가
  3. 코드길이 감소
  • 오버라이딩(Overriding) = 위에 올라탐, 재정의하는 것
package edu.kh.inheritance.model.vo;

public class Person extends Object {
	// class명에 Object에 대한 상속구문이 없다면
	// 컴파일러가 자동으로 extends Object 구문을 추가
	
	// 필드
	private String name; // 이름
	private int age; // 나이
	private String nationality; // 국적
	
	// 생성자
	public Person() {} // 기본생성자
	
	// 매개변수 생성자
	public Person(String name, int age, String nationality) {
		this.name = name;
		this.age = age;
		this.nationality = nationality;
	}

	// 메서드
	// getter/setter
	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getNationality() {
		return nationality;
	}

	public void setNationality(String nationality) {
		this.nationality = nationality;
	}
	
	
	public void breath() {
		System.out.println("사람은 코나 입으로 숨을 쉰다");
	}
	
	public void move() {
		System.out.println("사람은 움직일 수 있다.");
	}
	
	@Override
	public String toString() {
		return name + " / " + age + " / " + nationality;
	}
	
	

}
package edu.kh.inheritance.model.vo;

public class Student extends Person {
	// Student 클래스에 Person 클래스 내용을 연장한다
	// == Student 클래스에 Person 클래스 내용(필드, 메서드)을 추가하여 확장한다.
	
	// *** 상속 ***
	// extends : 확장하다, 연장하다 ..
	

	
//	private String name;
//	private int age;
//	private String nationality;
	private int grade; // 학년
	private int classRoom; // 반
	
	public Student() {
		
		// Student() 객체 생성 시
		// 내부에 상속받은 Person 객체를 생성하기 위한
		// Person 생성자 호출 코드 필요하다!
		
		// super : 상위
		// suer == Person
		//Person();
		super(); // super() 생성자
		// 부모의 생성자를 호출하는 코드
		// 반드시 자식생성자 최상단에 작성되어야 한다!
		// 부모의 생성자 실행(필드 초기화+특정기능 수행)
		
		// * super() 생성자 때문에
		// 자식 객체 내부에 부모 객체가 생성된다!
		
		// * super() 생성자 미작성시
		// 컴파일러가 컴파일 단계에서 자동으로 추가해줌.
		
		
	}
	
	public Student(String name, int age, String nationality, int grade, int classRoom) {
		
//		this.name = name;
//		this.age = age;
//		this.nationality = nationality;
		
		//this.name = name;
		// 왜? 부모의 필드에 private 접근제한자가 있어서
		// 아무리 물려받은 자식이라도 다른 객체이기 때문에 직접 접근 불가
		// -> 간접접근 방법 사용가능
		
		/*
		setName(name); setAge(age); setNationality(nationality);
		*/
		// 부모의 setter를 이용할 수 있지만 비효율적이다.
		super(name, age, nationality); // 부모의 생성자 중, 매개변수 생성자 호출
		
		this.grade = grade;
		this.classRoom = classRoom;
	}

	// shift + alt + s -> r -> selectAll -> Generate
	/*
	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getNationality() {
		return nationality;
	}

	public void setNationality(String nationality) {
		this.nationality = nationality;
	}
    */

	public int getGrade() {
		return grade;
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}

	public int getClassRoom() {
		return classRoom;
	}

	public void setClassRoom(int classRoom) {
		this.classRoom = classRoom;
	}
	
	@Override
	public String toString() {
		return super.toString() + " / " + grade + " / " + classRoom;
	}
	
	
}
package edu.kh.inheritance.model.vo;

public class Employee extends Person {
	
	// 필드
//	private String name;
//	private int age;
//	private String nationality;
	private String company;
	
	// 기본 생성자
	public Employee() {}
	
	// 매개변수 생성자
	public Employee(String name, int age, String nationality, String company) {
//		this.name = name;
//		this.age = age;
//		this.nationality = nationality;
		this.company = company;
	}
	
	/*
	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getNationality() {
		return nationality;
	}

	public void setNationality(String nationality) {
		this.nationality = nationality;
	}
	*/

	public String getCompany() {
		return company;
	}

	public void setCompany(String company) {
		this.company = company;
	}
	
	
	// Person으로부터 상속받은 메서드 중
	// move() 메서드를 Employee에 맞게 재정의(== 오버라이딩)
	
	// @Override 어노테이션 : 해당 메소드가 오버라이딩 되었음을
	// 						 컴파일러에게 알려주는 역할 -> 컴파일러에게 문법 체크를 하도록 알린다
	
	// 어노테이션(Annotation) : 컴파일러에게 알려주기 위한 코드 (컴파일러 인식용 주석)
	
	@Override
	public void move() {
		
		System.out.println("오버라이딩된 move()");
		System.out.println("효율적으로 빨리 일하고 움직인다");
	}
	
	@Override
	public String toString() {
		return super.toString() + " / " + company;
	}
	
	

}
package edu.kh.inheritance.model.service;

import java.util.Scanner;

import edu.kh.inheritance.model.vo.Employee;
import edu.kh.inheritance.model.vo.Person;
import edu.kh.inheritance.model.vo.Student;

public class InheritanceService {
	
	// 상속 확인 예제
	public void ex1() {
		// Person을 상속받은 Student가 Person 필드, 메소드를
		// 사용할 수 있는가?
		
		Person p = new Person();
		
		//p.name = "홍길동"; // private 때문에 직접접근불가(에러발생)
		
		p.setName("홍길동");
		p.setAge(25);
		p.setNationality("대한민국");
		
		System.out.println(p.getName()); // 홍길동
		System.out.println(p.getAge()); // 25
		System.out.println(p.getNationality()); // 대한민국
		
		
		System.out.println("-------------------------------------------");
		
		
		// Student 객체 생성
		Student std = new Student();
		
		// Student만의 고유한 필드
		std.setGrade(3);
		std.setClassRoom(5);
		
		// Person 클래스로부터 상속받은 필드, 메서드
		std.setName("고길동");
		std.setAge(19);
		std.setNationality("대한민국");
		
		System.out.println(std.getGrade()); // 3
		System.out.println(std.getClassRoom()); // 5
		
		// Student가 Person의 메소드 뿐 아니라 필드도 상속 받았는지 확인
		System.out.println(std.getName()); // 고길동
		System.out.println(std.getAge()); // 19
		System.out.println(std.getNationality()); // 대한민국
		
		System.out.println("-------------------------------------------");
		
		Employee emp = new Employee();
		
		// Employee만의 고유 메서드
		emp.setCompany("KH정보교육원");
		
		// Person 클래스로부터 상속받은 메서드
		emp.setName("조미현");
		emp.setAge(7);
		emp.setNationality("대한민국");
		
		System.out.println(emp.getCompany()); // KH정보교육원
		
		System.out.println(emp.getName()); // 조미현
		System.out.println(emp.getAge()); // 7
		System.out.println(emp.getNationality()); // 대한민국
		
		// 추가된 breath() 메서드 상속 확인하기
		p.breath(); // 사람은 코나 입으로 숨을 쉰다
		std.breath(); // 사람은 코나 입으로 숨을 쉰다
		emp.breath(); // 사람은 코나 입으로 숨을 쉰다
		// 상속의 특징 : 코드 추가 및 수정에 용이함
		// -> 부모에게 정의하면 상속받은 자식들은 모두 부모의 것을 그대로
		// 받아서 쓸 수 있음!

	}
	
	// super() 생성자 사용방법
	public void ex2() {
		
		// Student 매개변수 5개짜리 생성자
		Student std = new Student("김철수", 17, "한국", 1, 3);
		
		System.out.println(std.getName()); // 김철수
		System.out.println(std.getAge()); // 17
		System.out.println(std.getNationality()); // 한국
		
		System.out.println(std.getGrade()); // 1
		System.out.println(std.getClassRoom()); // 3
	}
	
	// 오버라이딩 확인 예제
	public void ex3() {
		
		Student std = new Student();
		Employee emp = new Employee();
		
		std.move(); // 오버라이딩 X -> Person의 메소드 수행
		
		emp.move(); // 오버라이딩 O -> Employee 메소드 수행
		
		
	}
	
	public void ex4() {
		// 모든 클래스는 Object 클래스의 후손
		// == 모든 클래스의 최상위 부모는 Object
		
		Person p = new Person();
		// Object를 상속받은 Person객체 생성
		
		Student std = new Student();
		// Person을 상속받은 Student객체 생성
		
		// Object - Person - Student
		
		
		System.out.println(p.hashCode());
		System.out.println(std.hashCode());
		// Person이 Object에서 물려받은 hashCode()를
		// Student가 또 물려받아 사용
		
		String str = "abc";
		Scanner sc = new Scanner(System.in);
		
		System.out.println(str.hashCode());
		// String - Object
		System.out.println(sc.hashCode());
		// Scanner - Object
		
	}
	
	public void ex5() {
		
		Person p = new Person("김철수", 17, "한국");
		
		System.out.println( p.toString() ); // 김철수 / 17 / 한국
		System.out.println( p ); // 김철수 / 17 / 한국
		// print 구문 수행 시 참조 변수명을 작성하면
		// 자동으로 toString() 메소드를 호출해서 출력한다!
		
		System.out.println("-----------------------------------");
		
		Student std = new Student("이백점", 18, "미국", 2, 6);
		
		System.out.println(std.toString()); // 이백점 / 18 / 미국 / 2 / 6

	}

}
package edu.kh.inheritance.run;

import edu.kh.inheritance.model.service.InheritanceService;

public class InheritanceRun {
	
	public static void main(String[] args) {
		
		InheritanceService service = new InheritanceService();
		
		//service.ex1();
		//service.ex2();
		//service.ex3();
		//service.ex4();
		service.ex5();
		
	}

}

09_다형성(Polymorphism)

  • 다형성 = 다양한 형태를 지닌 성질

package edu.kh.poly.ex1.model.vo;

public class Car {
	
	private String engine; // 엔진
	private String fuel; // 연료
	private int wheel; // 바퀴개수
	
	public Car() {}

	// alt + shift + s -> o -> enter
	public Car(String engine, String fuel, int wheel) {
		super();
		this.engine = engine;
		this.fuel = fuel;
		this.wheel = wheel;
	}

	public String getEngine() {
		return engine;
	}

	public void setEngine(String engine) {
		this.engine = engine;
	}

	public String getFuel() {
		return fuel;
	}

	public void setFuel(String fuel) {
		this.fuel = fuel;
	}

	public int getWheel() {
		return wheel;
	}

	public void setWheel(int wheel) {
		this.wheel = wheel;
	}
	
	// Object.toString() 오버라이딩
	@Override // 오버라이딩했다는걸 알려주는 Override 어노테이션
	public String toString() {
		return engine + " / " + fuel + " / " + wheel;
	}

	
	
	

}
package edu.kh.poly.ex1.model.vo;

public class Tesla extends Car{ // 전기차
	
	private int batteryCapacity; // 배터리 용량
	
	public Tesla() {}

	// alt + shift + s -> o -> 아래방향키 -> enter
	// 매개변수 생성자 + 상속받은것도 포함
	public Tesla(String engine, String fuel, int wheel, int batteryCapacity) {
		super(engine, fuel, wheel);
		this.batteryCapacity = batteryCapacity;
	}

	public int getBatteryCapacity() {
		return batteryCapacity;
	}

	public void setBatteryCapacity(int batteryCapacity) {
		this.batteryCapacity = batteryCapacity;
	}
	
	// Car.toString() 오버라이딩
	@Override
	public String toString() {
		return super.toString() + " / " + batteryCapacity;
	}
	
	
	
}
package edu.kh.poly.ex1.model.vo;

public class Spark extends Car{ // 경차

	private double discountOffer; // 할인혜택
	
	public Spark() {}

	public Spark(String engine, String fuel, int wheel, double discountOffer) {
		super(engine, fuel, wheel);
		this.discountOffer = discountOffer;
	}

	public double getDiscountOffer() {
		return discountOffer;
	}

	public void setDiscountOffer(double discountOffer) {
		this.discountOffer = discountOffer;
	}
	
	
	@Override
	public String toString() {
		return super.toString() + " / " + discountOffer;
	}
}
package edu.kh.poly.ex1.model.service;

import edu.kh.poly.ex1.model.vo.Car;
import edu.kh.poly.ex1.model.vo.Spark;
import edu.kh.poly.ex1.model.vo.Tesla;

public class PolyService {
	
	public void ex1() {
		// 다형성 확인 예제
		
		// Car 객체 생성
		Car car = new Car();
		// 부모타입 참조변수 = 부모객체
		
		// Tesla 객체 생성
		Tesla tesla = new Tesla();
		// 자식타입 참조변수 = 자식 객체
		
		// *************다형성*************
		Car car2 = new Tesla(); // 오류 발생 안함
		// Tesla 객체를 참조하는 변수의 타입이 Car(부모)이기 때문에 
		// Tesla 객체가 Car(부모) 객체로 변화함.
		
		Car car3 = new Spark();
		
		// 부모타입 참조변수 = 자식 객체
		
		// ***** 다형성(업캐스팅) 작성방법 *****
		// 1) 자식 객체가 부모 객체로 변하였기 때문에
		//  자식만의 고유한 필드, 메소드를 사용할 수 없다.
		
		// 1-1) car (부모 = 부모)
		car.setEngine("v6 6기통 엔진");
		car.setFuel("휘발유");
		car.setWheel(4);
		// Car 메소드 모두 사용 가능
		
		// 1-2) tesla (자식 = 자식)
		tesla.setEngine("전기모터");
		tesla.setFuel("전기");
		tesla.setWheel(4);
		tesla.setBatteryCapacity(1000000);
		// Tesla 메소드 모두 사용 가능
		
		// 1-3) car2 (부모 = 자식(Tesla))
		car2.setEngine("전기모터");
		car2.setFuel("전기");
		car2.setWheel(4);
		//car2.setBatteryCapacity(1000000);
		//The method setBatteryCapacity(int) is undefined for the type Car
		
		// 자식이 부모님 흉내낼 때 자신 것 사용 못하고, 부모님 것만 사용 가능하다!!!
		
		car3.setEngine("경차 엔진");
		car3.setFuel("휘발유");
		car3.setWheel(4);
		//car3.setDiscountOffer(0.5);
		
		
		// ----------------------------------------------------------
		
		
		// 2) 다형성을 이용한 객체 배열
		
		// 객체 배열: 같은 객체 참조 자료형의 변수를 하나의 묶음으로 다루는것
					// 부모타입 참조 자료형의 변수를 하나의 묶음으로 다루는것
		
		Car[] arr = new Car[3]; // 부모타입 참조변수 배열 선언 및 할당
		// 각 배열 요소가 Car 타입 참조변수
		
		arr[0] = car; // Car 주소
		
		arr[1] = car2; // Tesla 주소
		
		arr[2] = car3; // Spark 주소
		
		for(int i = 0; i < arr.length; i++) {
			System.out.println(i + "번째 인덱스의 엔진" + arr[i].getEngine());
		}
		
		// 상속 + 다형성
		// 상속 특징 : Car 상속클래스는 모두 getEngine()을 가지고있다를 정의
		// 다형성 (업캐스팅): 부모타입 참조변수 arr[i]로 자식 객체를 참조할 수 있다.
		
	}
	
	public void ex2() {
		// 2) 다형성(업캐스팅)을 이용한 매개변수 사용법
		Tesla t = new Tesla("전기모터", "전기", 4, 10000000);
		
		Spark s = new Spark("경차 엔진", "휘발유", 4, 0.5);
		
		Car c = new Car("경유엔진", "경유", 12);
		
		printCar(t);
		printCar(s);
		printCar(c);
		
		System.out.println("----------------------------------------");
		
		// 4) 다형성을 이용한 반환형 사용법
		//Car[] arr = { new Car(), new Tesla(), new Spark() };
		
		Car[] arr = { createCar(1), createCar(2), createCar(3) };
					//   Car            // Car        // Car
									    // (Tesla)    // (Spark)
		
		// arr[0]; // Car
		// arr[1]; // Tesla
		// arr[2]; // Spark
		
		// instanceof 연산자 : 객체의 자료형을 검사하는 연산자
		// -> 참조하는 객체가 특정 자료형이거나 부모쪽 상속관계인지 확인
		
		System.out.println( arr[1] instanceof Tesla ); // true
		System.out.println( arr[1] instanceof Spark ); // false
		System.out.println( arr[1] instanceof Car ); // true
		
	}
	
	// 전달받은 Car 또는 자식 객체의 엔진, 연료, 바퀴 개수를 출력하는 메서드
	// 매개변수에 부모타입 참조변수를 작성하면 모든 자식 객체를 전달받을 수 있다.
	public void printCar(Car temp) {
		// 부모타입 참조변수 = 자식타입객체
		// 다형성의 업캐스팅 모양과 똑같다.
		// temp에는 Tesla, Spark, Car 의 주소가 넘어와도 된다 (업캐스팅)
		
		System.out.println("엔진 : " + temp.getEngine());
		System.out.println("연료 : " + temp.getFuel());
		System.out.println("바퀴 갯수 : " + temp.getWheel() + "개");
		System.out.println();
		
	}
	
	public Car createCar(int num) {
		
		Car result = null;
		
		switch(num) {
		case 1 : result = new Car(); break;
		case 2 : result = new Tesla(); break;
		case 3 : result = new Spark(); break;
		}
		
		return result;
		
	}
	
	public void ex3() {
		
		// ********* 다형성 중 다운캐스팅 *********
		
		// 다운캐스팅이란?
		// 부모타입 참조변수가 자식 객체를 참조하는 기술로
		// 업캐스팅 상태에서만 진행할 수 있다.
		// 부모 타입을 자식타입으로 "강제 형변환"해서
		// 자식 객체의 본래 필드, 메소드를 사용 가능하게 한다.
		
		Car c1 = new Tesla("전기모터", "전기", 4, 50000);
		
		System.out.println( ((Tesla)c1).getBatteryCapacity() );
		// 주의 ! "." 연산자가
		// (Tesla) 형변환 연산자보다 우선순위가 높음.
		
		Tesla t1 = (Tesla)c1;
		
		System.out.println( t1.getBatteryCapacity() );
		
		
	}
	
	public void ex4() {
		// 다운 캐스팅 주의 사항!!
		
		Car c1 = new Tesla();
		
		//Spark s1 = (Spark)c1;
		
		// -> c1이 참조하는 객체는 Tesla인데
		// Spark 참조변수로 Tesla를 참조하려고 하면 문제가 있음.
//		java.lang.ClassCastException : 형변환 예외
		
		
		// 해결방법 : instanceof 와 같이 사용해야한다!
		
		if(c1 instanceof Spark) {
			Spark s1 = (Spark)c1; // 다운캐스팅
			System.out.println("성공");
		} else {
			System.out.println("실패 (Spark 타입이 아님)");
		}
		
	}
	
	public void ex5() {
		
		// 바인딩 (Biding)
		// 실제 실행할 메소드 코드와 호출하는 코드를 연결 시키는 것
		
		Car c1 = new Car("경유엔진", "경유", 8);
		
		System.out.println( c1.getEngine() );
		// Car 객체에 있는 getEngine() 메소드를 호출 == 바인딩
		// String edu.kh.poly.ex1.model.vo.Car.getEngine()
		
		// 프로그램 "실행 전"
		// - 컴파일러는 getEngine() 메소드가 Car에 있는걸로 인식해서
		// c1.getEngine()호출코드와
		// String edu.kh.poly.ex1.model.vo.Car.getEngine() 메소드 코드를 연결
		// -> 정적 바인딩
		
		System.out.println( c1.toString() );
		// String edu.kh.poly.ex1.model.vo.Car.getEngine()
		// Car 참조변수 c1을 이용해서
		// Car 객체에 있는 오버라이딩된 toString() 메소드를 호출
		
		// ** 다형성 적용시 바인딩 **
		Car c2 = new Spark("경차엔진", "휘발유", 4, 0.5);
		// 업캐스팅 적용 -> 부모 부분만 참조 가능한 상태
		
		System.out.println( c2.toString() ); // 경차엔진 / 휘발유 / 4 / 0.5
		// String edu.kh.poly.ex1.model.vo.Car.toString()
		
		// 참조변수 c2가 Car타입이므로
		// toString()도 Car의 toString()을 호출 - 정적 바인딩
		
		// 하지만 실행해보면 자식(Spark)의 toString()이 호출되는것을 확인
		// -> 컴파일 시에는 부모(Car)와 바인딩 == 정적바인딩
		// -> "실행 시" 에는 자식(Spark)의 오버라이딩된 메소드와 바인딩 == 동적바인딩

		
	}
}
package edu.kh.poly.ex1.run;

import edu.kh.poly.ex1.model.service.PolyService;

public class PolyRun {

	public static void main(String[] args) {
		
		PolyService service = new PolyService();
		
		//service.ex1();
		//service.ex2();
		//service.ex3();
		//service.ex4();
		service.ex5();
	}
}

0개의 댓글