본문 바로가기
공부/JAVA

[JAVA] Extends 와 Super

by yeaseul912 2022. 6. 22.
728x90

자바 코딩을 하다보면 무조건 보게 되는 extends 와 super

정확한 뜻을 몰라서 일단 되는 대로 썼지만.. 이제는 제대로 알고 넘어갈 때가 된것같다.!


아래와 같이 서로 다른 클래스들이 상속관계를 갖고 있다고 가정해보자. 

 

1. <K extends T>, <? extends T>

이 것은 T 타입을 포함한 자식(자손) 타입만 가능하다는 의미다. 즉, 다음과 같은 경우들이 있다.

<T extends B>	// B와 C타입만 올 수 있음
<T extends E>	// E타입만 올 수 있음
<T extends A>	// A, B, C, D, E 타입이 올 수 있음
 
<? extends B>	// B와 C타입만 올 수 있음
<? extends E>	// E타입만 올 수 있음
<? extends A>	// A, B, C, D, E 타입이 올 수 있음

상한 한계. 즉 extends 뒤에 오는 타입이 최상위 타입으로 한계가 정해지는 것이다.

 

대표적인 예로는 제네릭 클래스에서 수를 표현하는 클래스만 받고 싶은 경우가 있다.

Integer, Long, Byte, Double, Float, Short 같은 래퍼 클래스들은 Number 클래스를 상속 받는다.

 

즉,  Integer, Long, Byte, Double, Float, Short 같은 수를 표현하는 래퍼 클래스만으로 제한하고 싶은 경우 다음과 같이 쓸 수 있다.

public class GenericClass <K extends Number> { ... }

 

이렇게 특정 타입 및 그 하위 타입만 제한 하고 싶을 경우 쓰면 된다. 좀 더 구체적으로 예로 들자면, 다음과 같다. Integer는 Number 클래스를 상속받는 클래스라 가능하지만, String은 Number클래스와는 완전 별개의 클래스이기 때문에 에러(Bound mismatch)를 띄운다.

public class GenericClass <K extends Number> { 
	... 
}
 
public class Main {
  public static void main(String[] args) {

    GenericClass<Double> a1 = new GenericClass<>();	// OK!
 
    GenericClass<String> a2 = new GenericClass<>();	// error!
    
  }
}

 

2. <K super T>, <? super T>

이 것은 T 타입의 부모(조상) 타입만 가능하다는 의미다. 즉, 다음과 같은 경우들이 있다.

<K super B>	// B와 A타입만 올 수 있음
<K super E>	// E, D, A타입만 올 수 있음
<K super A>	// A타입만 올 수 있음
 
<? super B>	// B와 A타입만 올 수 있음
<? super E>	// E, D, A타입만 올 수 있음
<? super A>	// A타입만 올 수 있음

하한 한계. 즉 super 뒤에 오는 타입이 최하위 타입으로 한계가 정해지는 것이다.

 

대표적으로는 해당 객체가 업캐스팅(Up Casting)이 될 필요가 있을 때 사용한다.

예로들어 '과일'이라는 클래스가 있고 이 클래스를 각각 상속받는 '사과'클래스와 '딸기'클래스가 있다고 가정해보자.

 

이 때 각각의 사과와 딸기는 종류가 다르지만, 둘 다 '과일'로 보고 자료를 조작해야 할 수도 있다. (예로들면 과일 목록을 뽑는다거나 등등..) 그럴 때 '사과'를 '과일'로 캐스팅 해야 하는데, 과일이 상위 타입이므로 업캐스팅을 해야한다. 이럴 때 쓸 수 있는 것이 바로 super라는 것이다.


뭐야 나 왜 이해 안돼

 

 

조금 더 현실성 있는 예제라면 제네릭 타입에 대한 객체비교가 있다.

알고리즘 구현을 하다보면 PriorityQueue(우선순위 큐), TreeSet, TreeMap 같이 값을 정렬하는 클래스 쓰는데, 정렬 기준을 자기 참조 비교(커스터마이징) 할 때 자주보는 Comparable 이다.

public class GenericClass <E extends Comparable<? super E> { ... }

 

E extends Comparable 부터 한 번 분석해보자. 

extends는 앞서 말했듯 extends 뒤에오는 타입이 최상위 타입이 되고, 해당 타입과 그에 대한 하위 타입이라고 했다.

그럼 역으로 생각해보면 이렇다. E 객체는 반드시 Comparable을 구현해야한다는 의미 아니겠는가?

 

public class SaltClass <E extends Comparable<E>> { ... }
 
public class Student implements Comparable<Student> {
	@Override
	public int compareTo(Person o) { ... };
}
 
public class Main {
	public static void main(String[] args) {
		SaltClass<Student> a = new SaltClass<>();
	}
}

이렇게만 쓴다면 E extends Comparable<E> 까지만 써도 무방하다.

즉, SaltClass의 E 는 Student 이 되어야 하는데, Comparable<Person> 의 하위 타입이어야 하므로 거꾸로 말해 Comparable을 구현해야한다는 의미인 것이다.

 

그러면 왜 Comparable<E> 가 아닌 <? super E> 일까?

잠깐 설명했지만, super E는 E를 포함한 상위 타입 객체들이 올 수 있다고 했다.

 

만약에 위의 예제에서 학생보다 더 큰 범주의 클래스인 사람(Person)클래스를 둔다면 어떻게 될까? 한마디로 아래와 같다면?

public class SaltClass <E extends Comparable<E>> { ... }	// Error가능성 있음
public class SaltClass <E extends Comparable<? super E> { ... }	// 안전성이 높음
 
public class Person {...}
 
public class Student extends Person implements Comparable<Person> {
	@Override
	public int compareTo(Person o) { ... };
}
 
public class Main {
	public static void main(String[] args) {
		SaltClass<Student> a = new SaltClass<>();
	}
}

쉽게 말하면 Person을 상속받고 Comparable 구현부인 comparTo에서 Person 타입으로 업캐스팅(Up-Casting) 한다면 어떻게 될까?

 

만약 <E extends Comparable<E>>라면 SaltClass<Student> a 객체가 타입 파라미터로 Student를 주지만, Comparable에서는 그보다 상위 타입인 Person으로 비교하기 때문에 Comparable<E>의 E인 Student보다 상위 타입 객체이기 때문에 제대로 정렬이 안되거나 에러가 날 수 있다.

 

그렇기 때문에 E 객체의 상위 타입, 즉 <? super E> 을 해줌으로써 위와같은 불상사를 방지할 수가 있는 것이다.

 

(이 부분은 중요한 것이 이후 PriorityQueue와 TreeSet 자료구조를 구현할때 이 부분을 이해하고 있어야 가능하다고 한다.)

 

<E extends Comparable<? super E>> 에 대해 설명이 조금 길었다. 이 긴 내용을 한 마디로 정의하자면 이렇다.

"E 자기 자신 및 조상 타입과 비교할 수 있는 E"

반응형

댓글