상속 & 다형성

박정호·2025년 1월 6일

Java

목록 보기
1/1
/* 상속
 * - 코드의 재사용성
 * - 멤버 변수와 메소드만 상속
 * 
 * 다형성
 * 1. 상위타입 변수 = new 자손생성자([..]);
 * 2. 특징
 * 		- 상송한 멤버 변수와 메소드 이외의 자식만의 멤버변수와 메소드 호출 불가
 * 		- 해결책: 자식만의 변수 선언 후 형변환(객체 타입 형변환)
 */

package step01.syntax;

class Parent{
	String id;
	int age;
	
	Parent(){
		super(); //Object 객체 생성 완료
		System.out.println("부모");
	}
	
	void printAll() {
		System.out.println(id);
		System.out.println(age);
	}
}

public class Step01Child extends Parent{
	String job = "자식만의 물건";
	Step01Child(){
		super(); //자동 추가되는 필수 코드, ()
		System.out.println("자식");
	}
	public static void main(String[] args) {
		/* Step01Childe 객체생성 순서
		 * Object 객체 생성 -> Parent 객체 생성 -> Step01Child 객체 생성
		 */
		Parent sc = new Step01Child();
		sc.id = "tester";
		//sc.job = "공유해줘라~"; 에러 상위타입 변수로 자손만의 멤버 호출 불가
		
		//sc2는 새로운 개게 생성일까? sc와 동일한 객체 참조?
		Step01Child sc2 = (Step01Child)sc;
		sc2.job = "공유해줘라~";
		
		System.out.println(sc==sc2); //true
		System.out.println(sc); 	//step01.Step01Child@24d46ca6
		System.out.println(sc2); 	//step01.Step01Child@24d46ca6
	}
}

위 코드를 보면 알 수 있듯이 == 연산자를 사용하여 비교하면 객체의 주소 값으로 비교한다. sc2는 Step02Child를 가지고 Parent타입으로 생성된 객체이고 이를 down-casting하여 sc2를 만들었기 때문에 같은 객체이다.

하지만 주의해야 할 점이 있다.

sc 객체가 Step01Child의 생성자로 생성이 되었지만 반환타입은 Parent이다. 이 때문에 Step01Child에 존재하는 멤버 변수에는 접근하지 못하여 sc.job 코드에 오류가 생긴다. job에 접근하고 싶다면 job을 멤버 변수로 가지는 클래스인 자식 클래스 타입으로 반환해야 한다.

이상한 점은 변수는 접근이 안되는데 메소드는 가능하다는 것이다.

public class Step01Child extends Parent{
	String job = "자식만의 물건";
	Step01Child(){
		super(); //자동 추가되는 필수 코드, ()
		System.out.println("자식");
	}
	
	//추가
	void printJob(){
		System.out.println(job);
	}
	
	public static void main(String[] args) {
		/* Step01Childe 객체생성 순서
		 * Object 객체 생성 -> Parent 객체 생성 -> Step01Child 객체 생성
		 */
		Parent sc = new Step01Child();
		sc.id = "tester";
		//sc.job = "공유해줘라~"; 에러 상위타입 변수로 자손만의 멤버 호출 불가
		
		//sc2는 새로운 개게 생성일까? sc와 동일한 객체 참조?
		Step01Child sc2 = (Step01Child)sc;
		sc2.job = "공유해줘라~";
		
		System.out.println(sc==sc2); //true
		System.out.println(sc); 	//step01.Step01Child@24d46ca6
		System.out.println(sc2); 	//step01.Step01Child@24d46ca6
		
		//추가
		sc.printJob();
	}
}

이건 가능하다. ㄷㄷ

== 연산자와 Equals()의 차이

public class EqualsTest {

	public static void main(String[] args) {
		String v1 = "fisa"; //메모리 생성
		String v2 = "fisa";
		String v3 = new String("fisa");
		String v4 = new String("fisa");
		
		// == : 주소값 비교 / equals : String 타입끼리의 내용값 비교
		System.out.println(v1 == v2); //true
		System.out.println(v1 == v3); //false
		System.out.println(v2 == v4); //false
		
		System.out.println(v1.equals(v2)); //true
		System.out.println(v1.equals(v3)); //true
		System.out.println(v2.equals(v4)); //true
	}

위 코드 결과를 보면

==연산자는 String 객체의 주소값을 비교한다.

equals 메서드는 String의 내용을 비교하고 있다.

하지만 아래 경우를 보자.

			Person p = new Person("유재석", 10);
		Person p2 = new Person("유재석", 10);
		System.out.println(p == p2); //false
		System.out.println(p.equals(p2)); //false

위 코드는 String 대신 생성한 Person 객체의 내용을 비교하는 코드이다. 위에서 String 객체와는 달리 객체의 데이터에 담긴 내용이 같아도 false가 출력된다.

궁금해서 equals()에 들어가 봤다.

public boolean equals(Object obj) {
        return (this == obj);
}

Object class안에 equals가 정의되어 있고 객체의 주소값을 비교하는 연산을 하는 것을 볼 수 있다.

그렇다면 왜 String은 다를까?

String에서 사용한 equals()를 들어가면 String 클래스 안에 따로 equals()가 정의되어 있다.

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        return (anObject instanceof String aString)
                && (!COMPACT_STRINGS || this.coder == aString.coder)
                && StringLatin1.equals(value, aString.value);
}

이 때문에 String에서는 내용 비교가 가능한 것이다. 그러면 이와 같은 로직을 우리가 직접 짜주면 객체의 내용도 비교할 수 있게 되는 것 아닌가?

class Person{
	private String name;
	private int age;
	
	//? 재정의
	/* 객체가 보유한 데이터가 동일한 경우 true
	 * 데이터: name과 age
	 * String타입(참조타입)과 int타입(기본) 두가지 다 비교
	 * 
	 */
	public boolean equals(Object o) {
		if(o instanceof Person) {
			Person p = (Person)o;
			if(name.equals(p.name) && age == p.age) {
				return true;
			}
		}
		return false;
	}
}

이제 Person 객체 간의 데이터 비교가 가능해졌다.

/*
 * == : 기본타입(순수 값 비교), 참조타입(객체의 주소값 비교)
 * instanceof: 참조타입 동일 여부 비교(타입 비교)
 * 			 : 주의사항 - 무관한 타입과 비교 불가(오류)
 * public boolean equals(Object o){}: 동일한 객체 타입의 실 데이터 값 비교 
 * 
 * java.lang.Object의 equals() 원조
 * - 생성된 객체의 주소값 비교 후 반환
 * - 이미 == 연산자로 비교 가능, 개발자들은 편집(재정의)
 * - 재정의 로직
 * 	- 동일의 타입의 객체에 한해서 내용값 동등여부 비교
 */

package step02.apply;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
class Person{
	private String name;
	private int age;
	
	//? 재정의
	/* 객체가 보유한 데이터가 동일한 경우 true
	 * 데이터: name과 age
	 * String타입(참조타입)과 int타입(기본) 두가지 다 비교
	 * 
	 */
	public boolean equals(Object o) {
		if(o instanceof Person) {
			Person p = (Person)o;
			if(name.equals(p.name) && age == p.age) {
				return true;
			}
		}
		return false;
	}
}

public class EqualsTest {

	public static void main(String[] args) {
		String v1 = "fisa"; //메모리 생성
		String v2 = "fisa";
		String v3 = new String("fisa");
		String v4 = new String("fisa");
		
		// == : 주소값 비교 / equals : String 타입끼리의 내용값 비교
		System.out.println(v1 == v2); //true
		System.out.println(v1 == v3); //false
		System.out.println(v2 == v4); //false
		
		System.out.println(v1.equals(v2)); //true
		System.out.println(v1.equals(v3)); //true
		System.out.println(v2.equals(v4)); //true
		
		Person p = new Person("유재석", 10);
		Person p2 = new Person("유재석", 10);
		System.out.println(p == p2); //false
		System.out.println(p.equals(p2)); //false
	}

}

0개의 댓글