5-2 ์ ๋ค๋ฆญ, ๋ฐฐ์ด๋ณด๋ค๋ ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ๋ผ, (+์ํผํ์ ๊ตฌํ๋ฐฉ๋ฒ)
by JiwonDev
- ๐จ์ ๋ค๋ฆญ invariant(๋ฌด๋ณ์ฑ), covariant(๊ณต๋ณ, extend), contravariant(๋ฐ๊ณต๋ณ, super) ์ ๋ํด ์ค๋ช
ํด์ฃผ์ธ์. ํํธ. ์ดํํฐ๋ธ ์๋ฐ Item31. PECS - producer๋ extends๋ก consumer๋ super๋ก ์ฌ์ฉํ์
๐๊ณต๋ณ ์ฑ์ง : A๊ฐ B์ ํ์ํ์ ์ผ ๋, ์ ๋ค๋ฆญ T<A> ๊ฐ T<B>์ ํ์ํ์ ์ธ๊ฐ?
๐๋ฐ๊ณต๋ณ ์ฑ์ง: A๊ฐ B์ ํ์ํ์ ์ผ ๋, ์ ๋ค๋ฆญ T<B> ๊ฐ T<A>์ ํ์ ํ์ ์ธ๊ฐ?
๐๋ฌด๋ณ์ฑ ์ฑ์ง: A๊ฐ B์ ํ์ํ์ ์ผ ๋, ์ ๋ค๋ฆญ T<A> ์ T<B> ํ์ ์ด ์๋ก ์๋ฌด๋ฐ ๊ด๊ณ๊ฐ ์์ผ๋ฉด ๋ฌด๋ณ์ฑ.
Object number = new Number() // ์ฐธ๊ณ ๋ก Integer์ Double์ Number๋ฅผ ์์ํ๋ค.
Number integer = new Integer()
Number double = new Double()
List<Object> error = new List<Number> // ๋ฌด๋ณ์ฑ์ด๋ผ ๋ถ๊ฐ๋ฅ. ์ปดํ์ผ ์๋ฌ
List<Number> error = new List<Integer> // ๋ฌด๋ณ์ฑ์ด๋ผ ๋ถ๊ฐ๋ฅ. ์ปดํ์ผ ์๋ฌ
List<? extends Number> foo3 = new ArrayList<Number>(); // ์๊ธฐ์์ ์ ๋น์ฐํ ๊ฐ๋ฅ
List<? extends Number> foo3 = new ArrayList<Integer>(); // ๊ณต๋ณ์ฑ ์์. ๊ฐ๋ฅ
List<? extends Number> foo3 = new ArrayList<Double>(); // ๊ณต๋ณ์ฑ ์์. ๊ฐ๋ฅ
List<? super Integer> foo3 = new ArrayList<Integer>(); // ์๊ธฐ์์ ์ ๋น์ฐํ ๊ฐ๋ฅ
List<? super Integer> foo3 = new ArrayList<Number>(); // ๋ฐ๊ณต๋ณ์ฑ ์์. ๊ฐ๋ฅ
List<? super Integer> foo3 = new ArrayList<Object>(); // ๋ฐ๊ณต๋ณ์ฑ ์์. ๊ฐ๋ฅ
/* ์ฐธ๊ณ ๋ก ๋ค์ด์บ์คํ
์ ์๋์ ๊ฐ์ด ์ฌ์ฉ์ ์ปดํ์ผ์ ๋์ง๋ง, ๋ฐํ์์ ClassCastException ๋ฐ์. */
var parent = new Parent();
var child = parent // ClassCastException. ๋ฐฉ์งํ๋ ค๋ฉด if(parent instanceof Child)๋ฅผ ์ฌ์ฉํด์ค์๋ค.
// ์์ฐ์๋ ๊ณต๋ณ์ฑ์ ์ ๊ณตํด์ฃผ๋ฉด ๋จ. extends
class ZooService{
public void train(Cage<? extends Animal> cage){
List<Animal> myCage = cage.getAll(); // Cage์์ ๊บผ๋ด์ Animalํ์
์ผ๋ก ๋ฐ์
}
}
ZooService zoo = new ZooService();
Cage<Tiger> tigerCage = new Cage<>();
Cage<Lion> lionCage = new Cage<>();
zoo.train(tigerCage) // ๊ณต๋ณ์ฑ์ ์ง์ํด์ฃผ๊ธฐ ๋๋ฌธ์ OK
zoo.train(lionCage)
์ฌ๊ธฐ์ ์ค์ํ์ ์, ๋ฐ์ดํธ์ฝ๋์์ผ๋ก๋ ์ ๋ค๋ฆญ์ ์ฐ๋ ์์ฐ๋ ๋๊ฐ๋ค๋ ์ฌ์ค. ์ปดํ์ผ ์์ ์ ํ์ ์ด ๊ฒฐ์ ๋จ.
Cage<..>์ ๋ด์์ ์ฃผ๋๊ฑด ์ด์ฐจํผ ๋ฐ์ดํธ์ฝ๋๋ก๋ Object๋ผ์, ์ฌ์ฉํ๋๋ฐ ์๋ฌด๋ฐ ๋ฌธ์ ๊ฐ ์์
๐งจ ๋ฌธ์ ๋ ๊ทธ๋ฅ push(T animal) ์ด๋ผ๊ณ ์ ์ด๋ฒ๋ฆฌ๋ฉด, T๊ฐ ์ด๋ค ํ์ํ์
์ธ์ง ์ ์ ์์ด์ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋์.
์ ๋ค๋ฆญ์ ํ์
์ ๋์ ์ผ๋ก ๋ณํ์์ผ์ฃผ๋ ๊ธฐ๋ฅ์ด ์๋. <Tiger>๋ ๊ด์ฐฎ์ง๋ง <? extends Animal>์ ๋ฌธ์ ๊ฐ ๋ฐ์ํจ.
์ ๋ค๋ฆญ์ ์ปดํ์ผ์๊ฐ์ ํ์
์์ ์ฑ์ ๋ณด์ฅํ๊ธฐ์ํ ๊ธฐ๋ฅ์. ํ์
์ด ๋ค๋ฅด๋ฉด ๋น์ฐํ ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์.
ํ์ํ์
์ธ Tiger๊ฐ ์์์ธ Animal์ ๋ณํ์ด ๊ฐ๋ฅํด์ ์๊ด์์ / ํ์ง๋ง Tiger๋ฅผ Lion์ผ๋ก ๋ณ๊ฒฝ? ๋ถ๊ฐ๋ฅ. ์ด๋ฐ ์ค์๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐ์์์ผ์ค.
๊ทธ๋ด ๋ ์์ฐ์(Cage) ์ฝ๋ ํด๊ฒฐํ๋ ค๊ณ ํ์ง๋ง๊ณ ์๋น์, ์ฆ ํ์ ์ ์ฌ์ฉํ๋ ์ชฝ์์ super๋ฅผ ์ฌ์ฉํด์ ํด๊ฒฐํ๋ฉด ๋จ.
Cage<Tiger> tigerCage = new Cage<>();
Cage<? super Tiger> cage = tigerCage; // ์ด๊ฑด <? extends Animal>๊ณผ ๊ฐ์.
cage.push(new Tiger()); // ์ฌ๊ธฐ์ ์ฐจ์ด๊ฐ ์๋ค. cage.push(T Animal) ์ด ๊ฐ๋ฅํจ.
ํ์ํ์ ์ Lion์ผ๋ก ๊ฒฐ์ ํ๋ฉด, Dog๋ก ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํจ. ์ปดํ์ผ๋ฌ๋ ๋ฐฉ๋ฒ์ด ์์.
ํ์ง๋ง ์์ํ์
์ Tiger๋ฅผ ๋ฃ๋ Animal์ ๋ฃ๋ Object๋ฅผ ๋ฃ๋ ์๊ด์์. ์บ์คํ
ํ๋ฉด ๋๋๊น(๋ฐ์ดํธ์ฝ๋๋ object ์ฌ์ฉ)
Producer-extends, Consumer-super. ์ค์ฌ์ ํจ-์ฐ (PECS)
- ๐จList<T>, ์ ๋ค๋ฆญ ํ์ ์ ๋ณด(super-type ํ ํฐ)๋ ์๋ ์ฝ๋์ฒ๋ผ ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค. ํ์ง๋ง ์คํ๋ง์์๋ ์ฌ์ฉ ๊ฐ๋ฅํ๊ฒ ๊ตฌํ์ ํด๋จ๋๋ฐ, ์ด๋ป๊ฒ ์ฌ์ฉํ ์ ์์๊น์?
f.put(myList, List<String>.class);
๐ ์ฐ์ , Object๋ก ๋ฐ์์ ํ์ ๋ณํ์ ํ๋ ๋ฐฉ๋ฒ๋ ์์ผ๋, ๋งค์ฐ ์ํํ ๋ฐฉ๋ฒ์. ๋ฐํ์ ์๋ฌ๋ฅผ ๋ง๋ค๊ธฐ ์ฌ์.
๐ ๊ทธ ๋์์ผ๋ก ๋์จ๊ฒ ํ์
ํ ํฐ, ์ฆ Class<T> ๋ฅผ ๊ฐ์ด ์ค์ ๊ทธ๊ฑธ๋ก ์ ๋ค๋ฆญ ํ์
์ ๊ฒฐ์ ํ๊ณ , .cast()๋ก ์์ ํ๊ฒ ๋ณํ.
๋ฐฐ์ด๊ณผ ๋ฆฌ์คํธ์ ์ฐจ์ด
์๋ฐ์๋ ๋จ์ ๋ฐฐ์ด์ ์ ๊ณตํด์ฃผ๋, ๊ฐ๋ฅํ๋ฉด ์ปฌ๋ ์ ์ ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ์.
๋ฌผ๋ก ๊ธฐ๋ฅ์ ์ธ ์ฐจ์ด - (๋ฐฐ์ด์ ํฌ๊ธฐ๋ฅผ ๋์ ์ผ๋ก ๋๋ฆด ์ ์์)๋ ์์ง๋ง ๋ฐฐ์ด์ ํ์ ์์ ์ฑ์ ๋ณด์ฅํ ์ ์๊ณ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ ๊ณต๋ณ์ฑ , ์ฆ ํ์ํ์ ์ ๋ฃ์ ์ ์๋ค๋ ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
Object[] objects = new Long[1]; // ๊ฐ๋ฅ. ๋ค๋ง ๋ฐํ์๋ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์.
List<Object> list = new ArrayList<Long>(); //์ปดํ์ผ ์๋ฌ
๋ฆฌ์คํธ๋ ์์ํ์ ์ ๋ฐฐ์ด์ ํ์ํ์ ๋ ๋ฐ์ ์ ์์ด, ๋ฐ์ดํฐ ์ฝ์ ์ ๋ํ ์๋ฌ๋ฅผ ๋ฐํ์์ ํ์ธ ๊ฐ๋ฅํ๋ค. ํ์ง๋ง, ๋ฆฌ์คํธ๋ ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํด์ ์ฝ๊ฒ ์ฐพ์์ ์๋ค.
๋ฌผ๋ก ๋ฆฌ์คํธ๋ ๋ํผํ์ ์ด๋ฏ๋ก ๋ฐฐ์ด๋ณด๋ค ์๋๊ฐ ๋๋ฆฌ๋ค. ํ์ง๋ง Collection์ ๋ด๋ถ์ ์ธ ์ต์ ํ๊ฐ ์ด๋ฏธ ๋์ด์๊ณ , ๋๋ถ๋ถ์ ์๋น์ค์์ ๋ฆฌ์คํธ -> ๋ฐฐ์ด๋ก ์ต์ ํ๊ฐ ํ์ํ ๊ฒฝ์ฐ๋ ์๋ค. ํธ๋ ์ด๋ ์คํ๋ฅผ ๊ณ ๋ คํ์. ๋ฐฐ์ด์ ์น๋ช ์ ์ธ ๋จ์ ์ ๊ฐ์ง๋ฉด์๊น์ง ์ต์ ํ๊ฐ ํ์ํ ์๋น์ค์ธ๊ฐ?
# ์ด์์ด๋ฉด ์ ๋ค๋ฆญ ํ์ / ์ ๋ค๋ฆญ ๋ฉ์๋๋ก ๋ง๋ค์ด๋ผ.
์์ ๋ฐฐ์ด vs ๋ฆฌ์คํธ๋ฅผ ๋ณด๋ฉด ์๊ฒ ์ง๋ง, ์ ๋ค๋ฆญ์ ๋ฐํ์์ ์๋ฌด๋ฐ ์ํฅ์ ๋ผ์น์ง ์๊ณ ๋ ํ์ ์์ ์ฑ์ ๋ณด์ฅํ ์ ์๋ค. ์๋ก์ด ํ์ ์ผ๋ก ์ฌ์ฉํ ๊ฐ์ฒด๋ฅผ ์ค๊ณํ ๋ ํ๋ณํ(์บ์คํ )์ด ํ์ํ๋ค๋ฉด ์ ๋ค๋ฆญ์ผ๋ก ๋ง๋ค ์๋ ์๋์ง ๊ณ ๋ คํ์.
# ๋ฆฌ์คํธ์์ ๊ณต๋ณ์ฑ (ํ์ํ์ ๋ฐ๊ธฐ)๊ฐ ํ์ํ๋ฉด ํ์ ์ ์์ผ๋์นด๋๋ฅผ ์ฌ์ฉ
<? extends T>์ <? super T> ๋ฅผ ์ ๋ค๋ฆญ์ผ๋ก ์ฌ์ฉํ์.
public class CustomObject<E> {
private List<E> items;
public void customAddAll(List<? extends E> items) { // E์ E์ ํ์๊ฐ์ฒด
this.items.addAll(items);
}
public void copy(List<? super E> items){ // E์ E์ ์์ ๊ฐ์ฒด
items.addAll(this.items);
}
public void customRemoveAll(List<? extends E> items){
this.items.removeAll(items);
}
}
# ์ ๋ค๋ฆญ๊ณผ ๊ฐ๋ณ์ธ์(...)์ ๊ฐ์ด ์ฌ์ฉํ ๋๋ ์ ์คํ๋ผ.
์๋ฐ์์๋ ์๋์ ๊ฐ์ด ๊ฐ๋ณ์ธ์(์ธ์ ๊ฐ์๋ฅผ ์ ๋์ ์ผ๋ก) ๋ฐ์ ์ ์๋ค.
์ด ๊ฒฝ์ฐ ์ ๋ค๋ฆญ ๊ฐ๋ณ์ธ์์ ๊ฐ์ ์ ์ฅํ๋๊ฑด ์์ ํ์ง ์๋ค. ClassCastException์ ๋ฐ์์ํจ๋ค.
static void dangerous(List<String>... stringLists) {
Object[] objects = stringList; // ๊ฐ๋ณ์ธ์๋ฅผ ์ด๋ ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
// ๋ฏธ์น์ง์ด์ง๋ง ์๋์ ๊ฐ์ด ์ฌ์ฉํ๋ฉด ์ ๋ค๋ฆญ์ ์ปดํ์ผ ํ์
๊ฒ์ฆ์ ๋ฌด์ํด๋ฒ๋ฆฐ๋ค.
List<Integer> intList = List.of(42);
objects[0] = intList;
String s = stringList[0].get(0); // ์ฌ์ฉํ๋ ์์ ์ ClassCastException ๋ฐ์
}
๋ฌผ๋ก ๊ฐ๋ณ์ธ์ ๋ฐฐ์ด์ ์ธ๋ถ์ ๋ ธ์ถํ์ง์๊ณ , ์ฌ์ฉ์ ์ฃผ์ํ๋ค๋ฉด ํฐ ๋ฌธ์ ์์ด ์ฌ์ฉํ ์ ์๋ค.
์ด ๊ฒฝ์ฐ์๋ @SafeVarargs๋ฅผ ๋ฉ์๋์ ๋ถ์ฌ์ฃผ์. ์ด๋ @SupressWarnings์ ๋น์ทํ๊ฒ ์ปดํ์ผ ๊ฒฝ๊ณ ๋ฅผ ์ ๊ฑฐํด์ค๋ค.
๋ค๋ง ๋ฐ๋์ ์์ ํ๋ค๋๊ฑธ ๋ณด์ฅํด์ค์ผํ๋ค. @SafeVarargs๋ ํ์ํ๋ ์ฉ๋์ผ๋ฟ, ์ด๋ ํ ์ถ๊ฐ์์ ๋ ํ์ง ์๋๋ค.
public class Machine<T> {
private List<T> versions = new ArrayList<>();
@SafeVarargs
public final void safe(T... toAdd) {
for (T version : toAdd) {
versions.add(version);
}
}
}
# ํ์ ์์ ์ด์ข ์ปจํ ์ด๋ (ํ์ ์ ๋ณด Class<T>๋ฅผ ๋งค๊ฐ๋ณ์๋ก ํจ๊ป๋ฐ๊ธฐ)
์ ๋ค๋ฆญ์์ ์์ผ๋์นด๋๋ณด๋ค ํ์ ์ ์ ์ฐํ๊ฒ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์.
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type, T instance) {
//cast ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ํ์
์์ ์ฑ์ ๋ณด์ฅํ๋ค!
favorites.put(Objects.requireNonNull(type), type.cast(instance));
}
public <T> T getFavorite(Class<T> type) {
//cast ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ํ์
์์ ์ฑ์ ๋ณด์ฅํ๋ค!
return type.cast(favorites.get(type));
}
}
Favorites f = new Favorites();
f.putFavorite(String.class, "์คํธ๋ง");
f.putFavorite(Integer.class, 1);
f.putFavorite(Long.class, 1L);
// ๋ฌผ๋ก ์๋์ ๊ฐ์ด ์ค์ฒดํ๊ฐ ๋ถ๊ฐ๋ฅํ ์ ๋ค๋ฆญ์ ํ์
์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
// ์๋ฌ - f.putFavorite(List<String>.class, new ArrayList<>());
์ ๋ค๋ฆญ๊น์ง ํ์ ์ผ๋ก ๋ฐ๊ณ ์ถ๋ค๋ฉด ์ํผํ์ ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
- ParameterizedType๋ฅผ ์ฌ์ฉ
- TypeFactory๋ฅผ ์ฌ์ฉ
//์ํผํ์
์ ์ฌ์ฉํ์ฌ ํ์
์์ ์ฑ์ ๋ณด์ฅํ๋ค!
public <T> void putFavorite(TypeReference<T> valueTypeRef, T instance) {
// ParameterizedType๋ก ์บ์คํ
ํ์ฌ Class<T>๋ฅผ ์ป๋๋ค!
Class<T> tClass = ((Class<T>) ((ParameterizedType) valueTypeRef.getType()).getRawType());
favorites.put(Objects.requireNonNull(tClass), Objects.requireNonNull(instance));
}
//์ํผํ์
์ ์ฌ์ฉํ์ฌ ํ์
์์ ์ฑ์ ๋ณด์ฅํ๋ค!
public <T> T getFavorite(TypeReference<T> valueTypeRef) {
// TypeFactory๋ฅผ ์ด์ฉํด์ Class<T>๋ฅผ ์ป๋๋ค!
Class<T> rawClass = (Class<T>) TypeFactory.type(valueTypeRef.getType()).getRawClass();
return rawClass.cast(favorites.get(rawClass));
}
@Test
public void ์ด์ข
_ํ์
_์ปจํ
์ด๋_์ค์ฒดํ_๋ถ๊ฐ_ํ์
_์ํผํ์
_ํ ํฐ() {
//given
Favorites f = new Favorites();
// ์ํผํ์
์ผ๋ก ๋ฃ๋๋ค!
f.putFavorite(new TypeReference<List<String>>() {}, Collections.singletonList("์ํผ ํ์
ํ ํฐ"));
// ์ํผํ์
์ผ๋ก ๊บผ๋ธ๋ค!
List<String> strings = f.getFavorite(new TypeReference<List<String>>() {});
//then
assertEquals("์ํผ ํ์
ํ ํฐ", strings.get(0));
}
์ฐธ๊ณ ๋ก ์คํ๋ง์์๋ ParameterizedTypeReference ๋ฅผ ์ํผํ์ ์ผ๋ก ์ฌ์ฉํ๋ค. ๊ถ๊ธํ๋ค๋ฉด ์๋๋งํฌ๋ฅผ ์ฐธ๊ณ ํ์.
https://www.youtube.com/watch?v=01sdXvZSjcI
https://www.bsidesoft.com/2903
'๐ฑBackend > Effective-Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
7. ๋๋ค์ ์คํธ๋ฆผ, ๋ฉ์๋ (0) | 2021.10.06 |
---|---|
6-1 Enum๊ณผ @์ ๋ํ ์ด์ ์ ์ฌ์ฉํ๋ผ. (0) | 2021.10.06 |
5-1 ์ ๋ค๋ฆญ์ ๊ทธ๋ฅ Raw ๋ก ์ฌ์ฉํ์ง ๋ง๋ผ(+ ๋น๊ฒ์ฌ ๊ฒฝ๊ณ ์ ๊ฑฐ) (0) | 2021.09.15 |
4-1 ์์, ์ธํฐํ์ด์ค, ํด๋์ค์ ๊ถํ ์ต์ํ(+Java9 ๋ชจ๋) (0) | 2021.09.15 |
3-4 Cloneable์ ์ฌ์ฉํ์ง ๋ง๋ผ & Comparable์ ์ฌ์ฉ (0) | 2021.09.15 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev