210713 화
상속
*오버라이딩(14일에 이어서)
1) toString 오버라이딩
2) equals 오버라이딩
필드값 비교 가능
3) hashcode 오버라이딩
package com.kh.chap02_override.model.vo;
public class Book {
private String title;
private String author;
private int price;
public Book() {}
public Book(String title, String author, int price) {
super();
this.title = title;
this.author = author;
this.price = price;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public int getPrice() {
return price;
}
public void setTitle(String title) {
this.title = title;
}
public void setAuthor(String author) {
this.author = author;
}
public void setPrice(int price) {
this.price = price;
}
public String information() {
return "title : " + title + ", author : " + author +
", price : " + price;
}
//1. toString 오버라이딩
/*@Override
public String toString() {
return "title : " + title + ", author : " + author +
", price : " + price;
}*/
@Override
public String toString() {
return "Book [title=" + title + ", author="
+ author + ", price=" + price + "]";
}
//toString을 활용하면 bk1만 입력해도 출력가능
//2. equals 오버라이딩
//필드값 비교 가능
/*
@Override
public boolean equals(Object obj) { //이부분 다시 인강듣기**** 2번째 첫 시간
//this (현 객체) vs obj (비교할 대상 객체)
//Book Objcet
Book other = (Book)obj; //Object 타입의 비교 대상 객체를 Book으로 형변환
if(title.equals(other.getTitle())
&& author.equals(other.getAuthor())
&& price == other.getPrice()) {
return true;
}
return false;
}*/
// => 간단하게 구현 ver이었음
@Override //뭔 소리져..
public boolean equals(Object obj) {
//1. 주소값 비교 - 주소가 같으면 당연히 같은 객체 참조이므로 true 반환
if (this == obj)
return true;
//2. 주소값이 null이라면 참조하는게 없으므로 비교 가치가 없으므로 false 반환
if (obj == null)
return false;
//3. 클래스 비교 - 참조 자료형이 같지 않으면 비교 가치가 없어서 false를 반환
if (getClass() != obj.getClass())
return false;
//여기까지 코드 도달 시 1차 합격
//각 필드 값들이 동일한지 비교하기 위해 Object => Book 형변환
Book other = (Book) obj;
//author 필드 값 비교 (String 타입)
if (author == null) { // this.author가 null이고
if (other.author != null) //other.author가 null이 아니면
return false; //필드 값이 다르므로 false리턴
} else if (!author.equals(other.author)) //String 클래스의 equals를 비교해서
return false; //필드 값이 다르므로 false 리턴
//기본 자료형이므로 값 비교 후 같지 않으면 false 리턴
if (price != other.price)
return false;
// title 필드 값 비교(String 타입) - author와 같은 구조
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
//위의 조건들을 모두 만족하지 못해서 코드가 여기까지 도달했다는 것은
//모든 필드 값이 일치한다는 의미이므로 true리턴
return true;
}
// 어려운 ver
@Override
public int hashCode() {
//해쉬코드 값이 충돌(중첩)현상이 생기지 않도록 소수로 계산(충돌을 줄임)
final int prime = 31;
int result = 1;
result = prime * result + ((author == null) ? 0 : author.hashCode());
result = prime * result + price;
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
}
package com.kh.chap02_override.run;
import com.kh.chap02_override.model.vo.Book;
public class Run {
public static void main(String[] args) {
Book bk1 = new Book("수학의 정석", "나수학", 100);
Book bk2 = new Book("칭찬은 고래도 춤추게 한다", "고래", 300);
System.out.println(bk1.information());
System.out.println(bk2.information());
/*title : 수학의 정석, author : 나수학, price : 100
title : 칭찬은 고래도 춤추게 한다, author : 고래, price : 300*/
//모든 클래스는 Object 클래스의 후손이다.
//즉, 최상위 클래스는 항상 Object
//Object 클래스에 있는 메소드를 오버라이딩해서 사용할 수 있음
//1. toString
//오버라이딩 전 : Object 클래스의 toString 실행
//=> 패키지명을 포함한 클래스 풀네임 + @ + 객체의 해쉬코드 16진수 값
//오버라이딩 후 : Book 클래스의 toString 실행
//=> 해당 객체가 가지고 있는 멤버 필드 값에 대한 정보
System.out.println(bk1.toString());
System.out.println(bk2.toString());
//앞으로 information 메소드 대신 to String 메소드를 오버라이딩 해서 사용
//출력문에서 어떤 레퍼런스를 출력하고자 할 때 JVM이 자동으로 레퍼런스.toString()을 호출
//따라서 레퍼런스 변수만 작성해도 알아서 toString이 호출 됨
System.out.println(bk1);
System.out.println(bk2);
/*
* Book [title=수학의 정석, author=나수학, price=100]
Book [title=칭찬은 고래도 춤추게 한다, author=고래, price=300]
Book [title=수학의 정석, author=나수학, price=100]
Book [title=칭찬은 고래도 춤추게 한다, author=고래, price=300]
*/
//2. equals
Book bk3 = new Book("수학의 정석", "나수학", 100);
//bk1이 가지고 있는 필드 값과 완전하게 같은 필드 값을 가진 bk3 생성
System.out.println("bk1과 bk3가 같은 책입니까?" + (bk1 == bk3));
//딱 봐도 false가 나올 것임을 예상해야 한다.
//why?new 생성자로 다른 객체를 만들어줬기 때문에
System.out.println("bk1과 bk3가 같은 책입니까?" + (bk1.equals(bk3)));
//equals는 object가 가지고 있는 메소드이다.
//equals는 값 비교이다
//오버라이딩 전 : Object 클래스의 equals 호출 시 주소값을 비교하므로
//필드 값이 모두 같아도 false를 리턴함
//오버라이딩 후 : 실제 멤버 값을 비교하여 멤버 값이 같을 경우 true를 리턴함
/*
* bk1과 bk3가 같은 책입니까?false
bk1과 bk3가 같은 책입니까?true
*/
//3. hashCode
System.out.println(bk1.hashCode());
System.out.println(bk2.hashCode());
System.out.println(bk3.hashCode());
//오버라이딩 전 : Object 클래스의 hashCode => 객체의 실제 주소값을 10진수로 계산한 결과 값
//오버라이딩 후 : 멤버 값이 같은 경우 같은 해쉬 코드 값이 나오도록 처리
//동일 객체 : 실제 값도 같고 해쉬 코드도 같은 경우
//동등 개체 : 실제 값은 같지만 해쉬 코드가 다른 경우(다른 객체이지만 필드 값만 같은 경우)
//동등 객체 중복 제거 등의 로직에서 hash코드 판별 후 equals 판별
/*오버라이딩 전
* 705927765
366712642
1829164700
*/
//--------------------------------
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);//주소값 비교 => false
System.out.println(str1.equals(str2));//값 비교 => true
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
}
}
다형성
: 부모타입 레퍼런스로 자식 객체를 다루는 기술.
: 객체지향 프로그래밍의 3대 특징 중 하나로 ‘여러 개의 형태를 갖는다’는 의미. 부모타입 레퍼런스로 자식타입 레퍼런스를 다루는 기술
Parent p = new Child1(); Parent p2 = new Child2(); |
1) 부모, 자식 접근 클래스 예시
(1) Parent P = new Parent(); (O)
(2) Child c = new Child(); (O)
(3) Parent p = new Child(); (O)
(4) Child c = new Parent(); (X)
부모가 자식으로 들어가는 것은 불가능하다.
2) 캐스팅
(1) 업 캐스팅
: 자식 타입이 부모타입으로 형변환이 되는 것.
-업 캐스팅은 자동 형변환이 된다
chr c = new Sonata();
(2) 다운 캐스팅
: 부모 타입이 자식 타입이 될 때
예)
Car c = new Sonata();
((Sonata)c).moveSonata();
*instanceof 연산자
: 현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인 할 때 사용.
-클래스 타입이 맞으면 true, 맞지 않으면 false 반환.
3) 다형성을 이용할 때 좋은 점
-다형성을 이용하여 상속 관계에 있는 하나의 부모 클래스 타입의 배열 공간에 여러 종류의 자식 클래스 객체 저장 가능
Car[] carArr = new Car[5]; carArr[0] = new Sonata(); carArr[1] = new Avante(); carArr[2] = new Grandure(); carArr[3] = new Spark(); carArr[4] = new Morning(); |
4) 매개변수와 다형성
public void execute() {
driveCar(new Sonata());
driveCar(new Avante());
driveCar(new Grandure());
}
public void driveCar(Car c) {}
*바인딩
1) 동적 바인딩
: 컴파일 시 정적 바인딩 된 메소드를 실행할 당시의 객체 타입을 기준으로 바인딩 되는 것
(1) 동적 바인딩 성립 요건
: 상속 관계로 이뤄져 다형성이 적용된 경우, 메소드 오버라이딩이 되어 있으면 정적으로 바인딩 된 메소드 코드보다 오버라이딩 된 메소드 코드를 우선적으로 수행한다
2) 정적 바인딩
package com.kh.chap1_poly.part01_basic.model.vo;
public class Parent {
private int x;
private int y;
public Parent() {}
public Parent(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void printParent() {
System.out.println("나 부모야");
}
public void print() {
System.out.println("나 부모야");
}
}
package com.kh.chap1_poly.part01_basic.model.vo;
public class Child1 extends Parent{
private int z;
public Child1() {}
public Child1(int x, int y, int z) {
super(x, y);
this.z = z;
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
public void printChild1() {
System.out.println("나 첫번째 자식이야");
}
@Override
public void print() {
System.out.println("나 첫번째 자식이야");
}
}
package com.kh.chap1_poly.part01_basic.model.vo;
public class Child2 extends Parent{
private int n;
public Child2() {}
public Child2(int x, int y, int n) {
super(x, y);
this.n = n;
}
public int getN() {
return n;
}
public void setN(int n) {
this.n = n;
}
public void printChild2() {
System.out.println("나 두번째 자식이야");
}
@Override
public void print() {
System.out.println("나 두번째 자식이야");
}
}
<실행>
package com.kh.chap1_poly.part01_basic.run;
import com.kh.chap1_poly.part01_basic.model.vo.Child1;
import com.kh.chap1_poly.part01_basic.model.vo.Child2;
import com.kh.chap1_poly.part01_basic.model.vo.Parent;
public class Run {
public static void main(String[] args) {
System.out.println("1. 부모 타입 레퍼런스로 부모 객체 다루는 경우");
Parent p1 = new Parent();
p1.printParent();
// => p1 레퍼런스로 Parent에만 접근 가능
System.out.println("2. 자식 타입 레퍼런스로 자식 객체 다루는 경우");
Child1 c1 = new Child1();
c1.printParent(); //자식은 부모에 접근 가능
c1.printChild1(); //자식은 본인에도 접근 가능
// *******지금부터 다형성 적용 되는 내용********
System.out.println("3. 부모 타입 레퍼런스로 자식 개체 다루는 경우");
Parent p2 = new Child1();
//대입 연산자를 기준으로 좌항, 우항의 타입이 다르지만 오류가 나지 않고 있다
//why? 자동으로 Parent타입으로 형변환이 이뤄졌다고 생각해야 함 => 자동 형변환, 묵시적 형변환
p2.printParent();
//=> p2레퍼런스로 Parent만 접근할 수 있다(현재 Parent 타입이므로)
// Q. Child1에 접근하고 싶다면? A. 강제 형변환, 명시적 형변환을 해줘야 한다
((Child1)p2).printChild1();
/* 상속 구조의 클래스들 간에는 형변환이 가능하다
*
* 1. UpCasting(자식 타입을 부모타입으로 변환시키는 것) : 형변환 생략 가능, 자동 형변환
* ex) Parent = new Child();
*
*
* 2. DownCasting(부모타입을 자식 타입으로 변환시키는 것) : 형변환 생략 불가, 강제 형변환
* ex) ((Child1)p2).printChild1();
*
*/
System.out.println("4. 자식 타입 레퍼런스로 부모 객체를 다루는 경우");
//Child1 c2 = new Parent();
//빨간줄 = 컴파일 에러라고 한다
//컴파일 에러(소스 상의 문법 오류) => 타입 불일치
//Child1 c2 = (Child1)new Parent();
//명시적으로 다운 캐스팅을 해주고 프로그램을 실행한다면?
//런타임 에러(프로그램 실행 시 발생하는 오류) 발생 => ClassCastException
//Cast 연산자 사용시 타입 오류
/*
* 1. 부모 타입 레퍼런스로 부모 객체 다루는 경우
나 부모야
2. 자식 타입 레퍼런스로 자식 객체 다루는 경우
나 부모야
나 첫번째 자식이야
3. 부모 타입 레퍼런스로 자식 개체 다루는 경우
나 부모야
나 첫번째 자식이야
4. 자식 타입 레퍼런스로 부모 객체를 다루는 경우
*/
//다형성을 사용하는 이유는?
//다형성 적용 전
Child1[] arr1 = new Child1[2];
arr1[0] = new Child1(1, 2, 4);
arr1[1] = new Child1(2, 3, 5);
Child2[] arr2 = new Child2[2];
arr2[0] = new Child2(2, 1, 5);
arr2[1] = new Child2(5, 7, 2);
System.out.println("=====다형성 적용 후 객체 배열로=====");
Parent[] arr = new Parent[4];
arr[0] = new Child1(1, 2, 4);
arr[1] = new Child1(2, 3, 5);
arr[2] = new Child2(2, 1, 5);
arr[3] = new Child2(5, 7, 2);
// => 하나의 부모 타입으로 다양한 자식 객체를 참조할 수 있음
// => 자식에게 접근할 때 하나의 부모 타입으로 접근할 수 있음
//각각의 메소드를 실행해보자
((Child1)arr[0]).printChild1();
((Child1)arr[1]).printChild1();
((Child2)arr[2]).printChild2();
((Child2)arr[3]).printChild2();
/*
* =====다형성 적용 후 객체 배열로=====
나 첫번째 자식이야
나 첫번째 자식이야
나 두번째 자식이야
나 두번째 자식이야
*/
System.out.println("=====반복문 이용해서 출력=====");
for(int i = 0 ; i < arr.length ; i ++) {
//Q. 각 인덱스 별로 어떤 자식 객체를 참조하고 있는지 어떻게 판별하지..?
// A. <instanceof 연산자>
//현재 레퍼런스가 어떤 클래스형의 주소를 참조하고 있는지 확인할 때 사용한다
//클래스 타입이 일치하면 true, 아니면 false를 리턴
if(arr[i] instanceof Child1) {
((Child1)arr[i]).printChild1();
}else if(arr[i] instanceof Child2) {
((Child2)arr[i]).printChild2();
}
}
/*
* =====반복문 이용해서 출력=====
나 첫번째 자식이야
나 첫번째 자식이야
나 두번째 자식이야
나 두번째 자식이야
*/
System.out.println("===== 향상 된 for문 이용해서 출력 =====");
//실제 참조하고 있는 타입의 메소드를 적절하게 실행시켜주기 위해
//instanceof연산자를 사용해서..(이 뒤에 못 씀)
for(Parent p : arr) {
if(p instanceof Child1) {
((Child1)p).printChild1();
}else if(p instanceof Child2) {
((Child2)p).printChild2();
}
}
/*
* ===== 향상 된 for문 이용해서 출력 =====
나 첫번째 자식이야
나 첫번째 자식이야
나 두번째 자식이야
나 두번째 자식이야
*/
System.out.println("=====오버라이딩 적용해서 출력");
for(int i = 0 ; i < arr.length ; i++) {
arr[i].print();
//=> 자동으로 실제 참조하고 있는 객체의 오버라이딩 된 메소드를 실횅함
//=> '동적 바이딩'이 일어났다
/*
* =====오버라이딩 적용해서 출력
나 첫번째 자식이야
나 첫번째 자식이야
나 두번째 자식이야
나 두번째 자식이야
*/
}
}
}
'웹개발 수업 > JAVA' 카테고리의 다른 글
[Day +17]다형성, 추상 클래스, 추상 메소드, 인터페이스 (0) | 2021.07.15 |
---|---|
[Day +16] 자바 1차 시험 (0) | 2021.07.14 |
[Day +14]상속 / 오버라이딩, 오버로딩, for each문 (0) | 2021.07.12 |
[Day +13]객체 / 오버로딩, 메소드, 객체배열 (0) | 2021.07.11 |
[Day +12]객체 / 필드, 전역변수, 지역변수, 매개변수, 예약어(static, final), 생성자, 오버로딩 (0) | 2021.07.11 |