일단 오늘의 내용은 전에 발행한 글이 그대로 이어집니다!!!
2025.01.06 - [Java공부(코딩)] - 코딩초보의 자바(Java)공부 23일차 { 제네릭 - Generic1 }
코딩초보의 자바(Java)공부 23일차 { 제네릭 - Generic1 }
오늘은 제네릭메서드에관한 공부를 해볼까합니다중요한것이니 잘보고 배워가시는게 있으면 좋겠습니다!제네릭이 필요한 이유public class IntegerBox { private Integer value; public Integer getValue() { return val
ddkk1120.tistory.com
타입 매개변수 제한
동물병원을 만들거다
조건 : 각 동물병원에는 그 동물만 들어갈수있다.
ex)강아지동물병원인데 고양이가들어가거나할수없다.
여기서 핵심은 `<T extends Animal>` 이다.
타입 매개변수 `T` 를 `Animal` 과 그 자식만 받을 수 있도록 제한을 두는 것이다. 즉 `T` 의 상한이 `Animal` 이 되는 것이다.
타입 매개변수 `T` 에는 타입 인자로 `Animal` , `Dog` , `Cat` 만 들어올 수 있다. 따라서 이를 모두 수용할 수 있는
`Animal` 을 `T` 의 타입으로 가정해도 문제가 없다.
따라서 `Animal` 이 제공하는 `getName()` , `getSize()` 같은 기능을 사용할 수 있음.
package generic.ex3;
import generic.animal.Animal;
public class AnimalHospital<T extends Animal> {
private T value;
public AnimalHospital(T value) {
this.value = value;
}
public void checkUp() {
System.out.println(value);
value.sound();
}
public void bigger(T target) {
if(value.getSize()>target.getSize()) {
System.out.println(value.getName()+"이 더 큼");
}
else if(value.getSize() < target.getSize()) {
System.out.println(target.getName()+"이 더 큼");
}
else {
System.out.println("둘의 크기는 같다");
}
}
}
메인코드
package generic.ex3;
import generic.animal.Cat;
import generic.animal.Dog;
public class HospitalMain {
public static void main(String[] args) {
Dog dog = new Dog("멍멍이", 15);
Dog dog2 = new Dog("멍멍이2", 18);
Cat cat = new Cat("냐옹이", 10);
AnimalHospital<Dog> dogHospital1 = new AnimalHospital<>(dog);
AnimalHospital<Dog> dogHospital2 = new AnimalHospital<>(dog2);
AnimalHospital<Cat> catHospital = new AnimalHospital<>(cat);
dogHospital1.checkUp();
dogHospital2.checkUp();
dogHospital1.bigger(dog2);
// dogHospital1.bigger(cat);강아지병원에 고양이가 들어가지못하게 막음
catHospital.checkUp();
}
}
Animal [name=멍멍이, size=15]
멍멍
Animal [name=멍멍이2, size=18]
멍멍
멍멍이2이 더 큼
Animal [name=냐옹이, size=10]
냐옹냐옹
제네릭 메서드
이번에는 특정메서드에만 제네릭이 들어오는
제네릭메서드에 대해 알아봅시다!
public class GenericMethod {
public static Object objectMethod(Object obj) {
System.out.println("obj print : "+obj);
return obj;
}
public static<T> T genericMethod(T t) {
System.out.println("generic print : "+t);
return t;
}
public static<T extends Number> T numberMethod(T t) {
System.out.println("numberPrint : "+t);
return t;
}
}
사용코드
package generic.ex4;
public class GenericMethodMain {
public static void main(String[] args) {
Integer i = 10;
Object obj = GenericMethod.objectMethod(i);//object는 모두의 부모
Integer d = (Integer)obj; //타입을 바꾸려면 다운캐스팅을해야함!
System.out.println(d);
String a = "동곤 자바";
String generic = GenericMethod.<String>genericMethod(a);
System.out.println(generic);
double b = 10.5;
long c = 11111199999L;
Double numberMethod = GenericMethod.numberMethod(b);
Long numberMethod2 = GenericMethod.numberMethod(c);
System.out.println(numberMethod);
System.out.println(numberMethod2);
}
}
obj print : 10
10
generic print : 동곤 자바
동곤 자바
numberPrint : 10.5
numberPrint : 11111199999
10.5
11111199999
**제네릭 타입**
정의: `GenericClass<T>`
타입 인자 전달: 객체를 생성하는 시점
예) `new GenericClass<String>`
**제네릭 메서드**
정의: `<T> T genericMethod(T t)`
타입 인자 전달: 메서드를 호출하는 시점
예) `GenericMethod.<Integer>genericMethod(i)`
인스턴스 메서드, static 메서드**
제네릭 메서드는 인스턴스 메서드와 static 메서드에 모두 적용할 수 있다.
class Box<T> { //제네릭 타입
static <V> V staticMethod2(V t) {} //static 메서드에 제네릭 메서드 도입
<Z> Z instanceMethod2(Z z) {} //인스턴스 메서드에 제네릭 메서드 도입 가능
}
class Box<T> {
T instanceMethod(T t) {} //가능
static T staticMethod1(T t) {} //제네릭 타입의 T 사용 불가능
static 메서드는 인스턴스 단위가 아니라 클래스 단위로 작동하기 때문에 제네릭 타입과는 무관하다.
따라서 static 메서드에 제네릭을 도입하려면 제네릭 메서드를 사용해야 한다.
}
타입매개변수 제한
타입매개변수를 Number로 제한
참고로 `Integer` , `Double` , `Long` 과 같은 숫자 타입이 `Number` 의 자식이다.
```java
public static <T extends Number> T numberMethod(T t) {}
제네릭 메서드 타입 추론
String generic = GenericMethod.<String>genericMethod(a);
이런한 형식으로 <> 로 타입인자를 전달하는일은 귀찮다.
String generic = GenericMethod.genericMethod(a);
이렇게 타입인자를 아예 전달하지 않아도 전달하는 인자(a)를보고
추론해서 String이라는 것을 알 수 있음.
제네릭메서드 활용
package generic.ex4;
import generic.animal.Animal;
public class AnimalMethod {
static <T extends Animal>void animal(T t){
System.out.println("동물이름 : "+t.getName() );
System.out.println("동물 크기 : "+t.getSize() );
t.sound();
}
static<T extends Animal> T getBigger(T t1,T t2) {
return t1.getSize() > t2.getSize() ? t1 : t2;
}
}
package generic.ex4;
import generic.animal.Animal;
import generic.animal.Cat;
import generic.animal.Dog;
public class AnimalMain2 {
public static void main(String[] args) {
Cat cat = new Cat("준냥이", 5);
Dog dog = new Dog("아롱이", 8);
Dog dog2 = new Dog("골든리트리버", 12);
AnimalMethod.animal(cat);
AnimalMethod.animal(dog);
Animal bigger = AnimalMethod.getBigger(dog2, dog);
System.out.println(bigger+"가 더 큽니다");
}
}
제네릭 타입과 제네릭 메서드의 우선순위
package generic.ex4;
import generic.animal.Animal;
public class ComplexBox<T extends Animal>{
private T animal;
public void set(T animal) {
this.animal = animal;
}
public<T> T printReturn(T t){
System.out.println("animal.className : "+animal.getClass().getName());
System.out.println("t.className : "+t.getClass().getName());
//t.getName()으로 바로호출불가능 (T는 상속받은 클래스가없음)
return t;
}
}
import generic.animal.Animal;
import generic.animal.Cat;
import generic.animal.Dog;
public class ComplexboxMain {
public static void main(String[] args) {
Dog dog = new Dog("멍멍이", 8);
ComplexBox<Animal> complexBox = new ComplexBox<>();
complexBox.set(dog);
Cat cat = new Cat("냐옹이이", 3);
Cat printReturn = complexBox.printReturn(cat);
System.out.println(printReturn);
}
}
animal.className : generic.animal.Dog
t.className : generic.animal.Cat
Animal [name=냐옹이이, size=3]
타입매개변수가 T로 동일하지만
제네릭메서드가 우선순위를 가짐!!!
와일드카드
public class WildcardEx {
static<T> void genericMethod(Box<T> box) {
System.out.println("T : "+box.get());
}
static void printWildcardV1(Box<?> box) {
System.out.println("T : "+box.get());
}
static<T extends Animal> void printGenericMethod2(Box<T> box) {
T t = box.get();
System.out.println("이름 : "+t.getName());
}
static void printWildcardV2(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 :"+animal.getName());
}
static <T extends Animal> T printAndReturnGeneric(Box<T> box) {
T t = box.get();
System.out.println("이름 = " + t.getName());
return t;
}
static Animal printAndReturnWildcard(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 = " + animal.getName());
return animal;
}
}
T : 하하
T : 하하
이름 : 냐옹이
이름 :냐옹이
이름 = 냐옹이
이름 = 냐옹이
제네릭 메서드와 와일드 카드를 비교할 수 있게 같은 기능을 각각 하나씩 배치해두었다.
와일드카드는 `?` 를 사용해서 정의한다.
코드는 뒤에서 하나씩 설명하겠다.
비제한 와일드카드
//이것은 제네릭 메서드이다.
//Box<Dog> dogBox를 전달한다. 타입 추론에 의해 타입 T가 Dog가 된다.
static <T> void printGenericV1(Box<T> box) {
System.out.println("T = " + box.get());
}
//이것은 제네릭 메서드가 아니다. 일반적인 메서드이다.
//Box<Dog> dogBox를 전달한다. 와일드카드 ?는 모든 타입을 받을 수 있다.
static void printWildcardV1(Box<?> box) {
System.out.println("? = " + box.get());
}
두 메서드는 비슷한 기능을 하는 코드이다. 하나는 제네릭 메서드를 사용하고 하나는 일반적인 메서드에 와일드카
드를 사용했다.
와일드카드는 제네릭 타입이나 제네릭 메서드를 정의할 때 사용하는 것이 아니다. `Box<Dog>` , `Box<Cat>` 처럼
타입 인자가 정해진 제네릭 타입을 전달 받아서 활용할 때 사용한다.
와일드카드인 `?` 는 모든 타입을 다 받을 수 있다는 뜻이다.
다음과 같이 해석할 수 있다. `? == <? extends Object>`
이렇게 `?` 만 사용해서 제한 없이 모든 타입을 다 받을 수 있는 와일드카드를 비제한 와일드카드라 한다.
여기에는 `Box<Dog> dogBox` , `Box<Cat> catBox` , `Box<Object> objBox` 가 모두 입력ㄷ가능
와일드카드2
상한 와일드카드
static <T extends Animal> void printGenericV2(Box<T> box) {
T t = box.get();
System.out.println("이름 = " + t.getName());
}
static void printWildcardV2(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 = " + animal.getName());
}
`box.get()` 을 통해서 꺼낼 수 있는 타입의 최대 부모는 `Animal` 이 된다. 따라서 `Animal` 타입으로 조회할 수
있다.
결과적으로 `Animal` 타입의 기능을 호출할 수 있다.
타입 매개변수가 꼭 필요한 경우
와일드카드는 제네릭을 정의할 때 사용하는 것이 아니다.
`Box<Dog>` , `Box<Cat>` 처럼 타입 인자가 전달된 제네릭 타입을 활용할 때 사용한다
static <T extends Animal> T printAndReturnGeneric(Box<T> box) {
T t = box.get();
System.out.println("이름 = " + t.getName());
return t;
}
static Animal printAndReturnWildcard(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 = " + animal.getName());
return animal;
}
`printAndReturnGeneric()` 은 다음과 같이 전달한 타입을 명확하게 반환할 수 있다.
Dog dog = WildcardEx.printAndReturnGeneric(dogBox)
반면에 `printAndReturnWildcard()` 의 경우 전달한 타입을 명확하게 반환할 수 없다.
여기서는 `Animal` 타입으로 반환한다.
Animal animal = WildcardEx.printAndReturnWildcard(dogBox)
정리하면 제네릭 타입이나 제네릭 메서드가 꼭 필요한 상황이면 `<T>` 를 사용하고,
그렇지 않은 상황이면 와일드카드를사용하는 것을 권장한다.
하한 와일드카드
public class WildCardMain2 {
public static void main(String[] args) {
Box<Object> box1 = new Box<>();
Box<Animal> box2 = new Box<>();
Box<Dog> box3 = new Box<>();
Box<Cat> box4 = new Box<>();
writeBox(box1);
writeBox(box2);
// writeBox(box3);
// writeBox(box4); 하한이 Animal
Object object = box1.get();
상한 제한 없이 선언한 타입 매개변수 `T` 는 `Object`로 변환된다.
// Animal animal1 = (Animal)object; Animal타입으로 바꾸려면강제형변환필요
System.out.println(object);
Animal animal2 = box2.get();
System.out.println(animal2);
}
static void writeBox(Box<? super Animal >t) {
t.setValue(new Dog("멍멍이", 12));
}
}
Animal의 아래(자식) 은
write에 들어갈수 없으며
오직 Animal의 상위타입만 들어간다.
문제와풀이
준비
여러분은 게임속 케릭터를 클래스로 만들어야 한다.
`BioUnit` 은 유닛들의 부모 클래스이다.
`BioUnit` 의 자식 클래스로 `Marine` , `Zealot` , `Zergling` 이 있다.
문제를 풀기 전에 우선 다음 코드를 완성하자.
package generic.test.ex3.unit;
public class Biounit {
private String name;
private int hp;
public Biounit(String name, int hp) {
super();
this.name = name;
this.hp = hp;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the hp
*/
public int getHp() {
return hp;
}
/**
* @param hp the hp to set
*/
public void setHp(int hp) {
this.hp = hp;
}
@Override
public String toString() {
return "Biounit [name=" + name + ", hp=" + hp + "]";
}
}
public class Marine extends Biounit {
public Marine(String name, int hp) {
super(name, hp);
// TODO Auto-generated constructor stub
}
}
public class Zelot extends Biounit{
public Zelot(String name, int hp) {
super(name, hp);
// TODO Auto-generated constructor stub
}
}
public class Zergling extends Biounit {
public Zergling(String name, int hp) {
super(name, hp);
// TODO Auto-generated constructor stub
}
}
문제와 풀이1 - 제네릭 메서드와 상한
**문제 설명**
다음 코드와 실행 결과를 참고해서 `UnitUtil` 클래스를 만들어라.
`UnitUtil.maxHp()` 메서드의 조건은 다음과 같다.
두 유닛을 입력 받아서 체력이 높은 유닛을 반환한다. 체력이 같은 경우 둘 중 아무나 반환해도 된다.
제네릭 메서드를 사용해야 한다.
입력하는 유닛의 타입과 반환하는 유닛의 타입이 같아야 한다.
public class UnitUtil {
public static<T extends Biounit> T maxHp(T name1,T name2){
return name1.getHp()>name2.getHp() ? name1 : name2;
}
public static void main(String[] args) {
Zelot zelot1 = new Zelot("질럿1", 70);
Zelot zelot2 = new Zelot("질럿2", 80);
Zelot maxHp = maxHp(zelot1, zelot2);
System.out.println(maxHp);
}
}
Biounit [name=질럿2, hp=80]
문제와 풀이2 - 제네릭 타입과 상한
**문제 설명**
다음 코드와 실행 결과를 참고해서 `Shuttle` 클래스를 만들어라.
`Shuttle` 클래스의 조건은 다음과 같다.
제네릭 타입을 사용해야 한다.
`showInfo()` 메서드를 통해 탑승한 유닛의 정보를 출력한다.
public class Shuttle<T extends Biounit> {
private T t;
public void in(T t) {
this.t = t;
}
public void showInfo() {
System.out.println(t.toString());
}
public static void main(String[] args) {
Shuttle<Biounit> shuttle = new Shuttle<>();
shuttle.in(new Marine("마린1", 40));
shuttle.showInfo();
}
}
문제와 풀이3 - 제네릭 메서드와 와일드카드
**문제 설명**
앞서 문제에서 만든 `Shuttle` 을 활용한다.
다음 코드와 실행 결과를 참고해서 `UnitPrinter` 클래스를 만들어라.
`UnitPrinter` 클래스의 조건은 다음과 같다.
`UnitPrinter.printV1()` 은 제네릭 메서드로 구현해야 한다.
`UnitPrinter.printV2()` 는 와일드카드로 구현해야 한다.
이 두 메서드는 셔틀에 들어있는 유닛의 정보를 출력한다.
(BioUnit이 toString되어있는걸 생각하자)
public class UnitPrinter {
public static<T extends Biounit> void printV1(Shuttle<T> value) {
T out = value.out();
System.out.println(out);
}
public static void printV2(Shuttle<? extends Biounit> t) {
Biounit out = t.out();
System.out.println(out);
}
}
public class UnitPrinterTest {
public static void main(String[] args) {
Shuttle<Biounit> shuttle1 = new Shuttle<>();
shuttle1.in(new Zelot("질럿1", 30));
Biounit out = shuttle1.out();
Shuttle<Biounit> shuttle2 = new Shuttle<>();
shuttle2.in(new Marine("마린1", 50));
Biounit out2 = shuttle2.out();
Biounit maxHp = UnitUtil.maxHp(out, out2);
System.out.println(maxHp);
UnitPrinter.printV1(shuttle1);
UnitPrinter.printV2(shuttle2);
}
}
Biounit [name=마린1, hp=50]
Biounit [name=질럿1, hp=30]
Biounit [name=마린1, hp=50]
'Java공부(코딩)' 카테고리의 다른 글
코딩초보의 자바(Java)공부 26일차 { 컬렉션프레임워크 - List } (0) | 2025.01.09 |
---|---|
코딩초보의 자바(Java)공부 25일차 { 컬렉션프레임워크 - ArrayList와LinkedList } (0) | 2025.01.08 |
코딩초보의 자바(Java)공부 23일차 { 제네릭 - Generic1 } (1) | 2025.01.06 |
코딩초보의 자바(Java)공부 22일차 { 예외처리 실습 } (0) | 2025.01.04 |
코딩초보의 자바(Java)공부 21일차 { 예외처리 } (1) | 2025.01.03 |