자바의 정석 12장 (31일차) - 와일드카드 <?> & 지네릭스 형변환

728x90

와일드 카드

일반적으로 지네릭스를 선언하면 대입된 타입이 일치해야 하는데, 와일드 카드를 사용하면 대입 변수가 불일치하여도 컴파일 오류가 발생하지 않는다.

 

대표적으로 아래와 같이 선언한다. 

  • <? extends Object obj> : obj와 그 자손들만 상속 가능 (상한적 제한)
  • <? super Object obj> : obj와 그 조상들만 상속 가능(하한적 제한)
  • <?> : 모든 Object 타입 가능, Object / E / T를 직접 선언한 것과 같음

 

1. <? extends Object obj> : 대입된 변수 obj와 그 자손들만 상속 가능

*plant2를 상속한 자손인 basil2와 rose만 지네릭스를 선언할 수 있다.

		ArrayList<? extends plant2> arr = new ArrayList<plant2>();
		ArrayList<? extends plant2> arr2 = new ArrayList<basil2>();
		ArrayList<? extends plant2> arr3 = new ArrayList<rose>();
	}
}

class nature{}
class plant2 extends nature{}
class basil2 extends plant2{}
class rose extends plant2{}

 

2. <? super Object obj> : 대입된 변수 obj와 그 조상들만 상속 가능

*plant2의 조상인 nature만 지네릭스를 선언할 수 있다.

		ArrayList<? super plant2> arr4 = new ArrayList<nature>();
		ArrayList<? super plant2> arr5 = new ArrayList<plant2>();
//		ArrayList<? super plant2> arr6 = new ArrayList<basil2>();
	}
}

class nature{}
class plant2 extends nature{}
class basil2 extends plant2{}
class rose extends plant2{}

 

3. <?> : 전부 선언가능

		ArrayList<?> arr7 = new ArrayList<nature>();
		
	}
}
class nature{}
class plant2 extends nature{}
class basil2 extends plant2{}
class rose extends plant2{}

지네릭스 형변환

지네릭스가 선언된 타입을 지네릭타입이라 하고 선언되지 않은 클래스 타입을 원시 타입이라 부른다. 

 

원시타입을 지네릭타입으로 형변환이 가능할까?

컴파일오류는 발생하지 않지만 지네릭타입을 원시타입으로 형변환할 경우에는 지네릭타입에 어떤 타입이 들어갈 지 모르기 때문에 경고를 발생시킨다.

 

t = (test)ObjT; // 원시 타입 t에 지네릭타입 ObjT을 형변환 하여 대입 > 경고
ObjT = (test<Object>)t; //지네릭 타입 objT에 test<Objct>로 원시타입 t를 형변환 하여 대입 > 경고

 

따라서 형변환시에는 와일드 카드가 선언된 지네릭타입으로만 형변환한다.

아래 예시처럼 와일드카드가 Object를 상속한 모든 타입을 사용하는 것을 허용하므로 어떤 타입이 들어와도 컴파일 오류가 발생되지 않는다.

*형변환 생략 가능

		test<? extends Object> t2 = (test<? extends Object>)new test();
		test<? extends Object> t3 = new test();
//		위의 두개의 문장은 동일한 의미, 다만 지네릭스에 제한을 둠으로써 모든 타입을 받아올 수 있기 때문에 생략을 허용
//		Object를 상속한 모든 변수를 허용함으로써 모든 대입 변수 허용
	}
}
class test<T>{}

 


지네릭 타입 제거

컴파일러가 실행될 시 지네릭타입은 제거되고 컴파일러가 필요한 곳에 자동으로 형변환을 시킨다. 

 

클래스의 지네릭스를 T 또는 E로 선언하고 컴파일러를 실행하면 T와 E가 Object로 자동으로 형변환된다.

  • 아래의 주석과 같이 T가 제거되면서 Object로 변경됨
class Test<T>{
	void add(T t) {
//	void add(Object obj)
	}
}

 

만약 지네릭스가 제한되어 있다면 어떤 타입으로 형변환이 될까?

 

만약 지네릭스가 제한되어있다면 그 제한된 class로 형변환이 되고 명확한 값이 출력될 때는 컴파일러가 자동으로 제한된 class로 형변환을 시켜준다.

class TestLimited<T extends Test>{
	String add(T t) {
//	void add(Test t)
		return "형변환필요but생략";
//		return (Test)"형변환필요but생략";
	}
}

 

728x90