반응형
1. Generic이 필요한 이유에 대하여
Integer를 담을 수 있는 Box
package generic.ex1;
public class IntegerBox {
private int value;
public void set(Integer value) {
this.value = value;
}
public Integer get() {
return value;
}
}
String을 담을 수 있는 Box
package generic.ex1;
public class StringBox {
private String value;
public void set(String object) {
this.value = object;
}
public String get() {
return value;
}
}
Integer와 String을 각 Box에 담아서 출력하는 Main 코드
package generic.ex1;
public class BoxMain1 {
public static void main(String[] args) {
// integer
IntegerBox integerBox = new IntegerBox();
integerBox.set(10);
Integer integer = integerBox.get();
System.out.println("integer = " + integer);
// string
StringBox stringBox = new StringBox();
stringBox.set("test");
String string = stringBox.get();
System.out.println("string = " + string);
}
}
// 실행 결과
integer = 10
string = test
1.1. 이 코드의 문제점
만약 Double, Boolean 등의 다른 타입을 담는 Box가 필요하다면 별도로 클래스를 만들어야 한다.
→ 재사용성이 매우 떨어진다.
2. 다형성을 통해 중복 해결을 시도하기
Object를 담는 Box 클래스
→ Object는 모든 클래스의 부모이기 때문에 모든것을 담을 수 있다.
package generic.ex1;
public class ObjectBox {
private Object value;
public void set(Object object) {
this.value = object;
}
public Object get() {
return value;
}
}
Object Box를 통해 String, Integer를 담는 Main 예제
package generic.ex1;
public class BoxMain2 {
public static void main(String[] args) {
// 1. Integer를 담기위한 Box를 생성한다.
ObjectBox integerBox = new ObjectBox();
integerBox.set(10);
// 1.1. Object -> Integer로 캐스팅해야 한다.
Integer integer = (Integer) integerBox.get();
System.out.println("integer is " + integer);
// 2. String을 담기 위한 Box를 생성한다.
ObjectBox stringBox = new ObjectBox();
stringBox.set("hello");
// 2.1. Object -> String으로 캐스팅 필요
String str = (String) stringBox.get();
System.out.println("string is " + str);
// 잘못된 타입의 인수 전달 시
integerBox.set("문자100");
Integer result = (Integer) integerBox.get();
System.out.println("result is " + result);
}
}
// 결과
integer is 10
string is hello
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
at generic.ex1.BoxMain2.main(BoxMain2.java:25)
Process finished with exit code 1
2.1. 문제점
- 자식은 부모를 담을 수 없다. → (Integer), (String) 처럼 타입 캐스팅 코드를 넣어서 Object 타입을 Integer, String 등으로 직접 다운 캐스팅을 해야 한다.
- 잘못된 타입의 인수 전달 문제 integerBox에 Integer가 들어가길 기대했다. 하지만 Java 입장에서는 아무런 문제가 되지 않는다. 따라서 타입 안정성 문제가 발생한다.
integerBox.set("문자100");
3. 제네릭 적용
제네릭을 사용하면 두마리 토끼를 잡을 수 있다.
코드 재사용 & 타입 안정성
3.1. 예제
GenericBox
package generic.ex1;
public class GenericBox<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
- <> : 다이아몬드 기호를 통해 제네릭 클래스임을 나타낸다.
- T : 타입 매개변수
Main 코드
package generic.ex1;
public class BoxMain3 {
public static void main(String[] args) {
// 생성 시점의 T의 타입이 결정된다.
GenericBox<Integer> integerBox = new GenericBox<Integer>();
// integerBox에 Integer만 넣을 수 있음 (타입 안정성)
// integerBox를 타입 캐스팅 변환하지 않아도 됌
integerBox.set(10);
Integer integer = integerBox.get();
System.out.println("integer = " + integer);
GenericBox<String> stringBox = new GenericBox<String>();
stringBox.set("Hello");
String str = stringBox.get();
System.out.println("str = " + str);
// 타입 추론 가능 : 생성하는 제네릭 타입 생략 가능
GenericBox<Integer> integerBox2 = new GenericBox<>();
}
}
// 결과
integer = 10
str = Hello
4. Generic 용어와 관례
Generic의 핵심 : 사용할 타입을 미리 결정하지 않는다는 점 (실제 사용하는 생성 시점에 타입을 결정하는 것)
4.1. 메서드의 매개변수와 인자
void method(String param) // 매개변수(Parameter)
void main() {
String arg = "hello"
method(arg) // 인수 (Argument)
}
매개변수(Parameter)를 정의하고 실행 시점에 인자(Argument)를 통해 원하는 값을 매개변수에 전달한다.
4.2. 제네릭의 타입 매개변수와 타입 인자
메서드의 매개변수와 유사하다.
- 메서드의 매개변수는 사용할 값에 대한 결정을 나중으로 미루는 것
- 제네릭의 타입 매개변수는 사용할 타입에 대한 결정을 나중에 미루는 것
즉, 제네릭 클래스는 타입 매개변수에 타입 인자를 전달해서 사용할 타입을 결정한다.
- 타입 매개변수 : GenericBox<T>에서 T
- 타입 인자
- GenericBox<Integer>에서 Integer
4.3. 용어 정리
- 제네릭 타입
- 클래스나 인터페이스를 정의할 때 타입 매개변수를 사용하는 것을 말한다.
- 제네릭 클래스, 제네릭 인터페이스를 모두 합쳐 제네릭 타입이라고 한다.
- 타입 매개변수
- 제네릭 타입이나 메서드에서 사용되는 변수로, 실제 타입으로 대체된다.
- 타입 인자
- 제네릭 타입을 사용할 때 제공되는 실제 타입이다.
4.4. 제네릭 명명 관례
- E : Element
- K : Key
- N : Number
- T : Type
- V : Value
5. 제네릭 활용 예제
Animal, Dog, Cat 클래스
// Animal
package generic.animal;
public class Animal {
private String name;
private int size;
public Animal(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public void sound() {
System.out.println("동물울음소리");
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\\'' +
", size=" + size +
'}';
}
}
// Cat
package generic.animal;
public class Cat extends Animal{
public Cat(String name, int size) {
// 부모 클래스에 정의된 생성자가 있기 때문에 맞추어 super 호출
super(name, size);
}
@Override
public void sound() {
System.out.println("냐옹");
}
}
// Dog
package generic.animal;
public class Dog extends Animal{
public Dog(String name, int size) {
// 부모 클래스에 정의된 생성자가 있기 때문에 맞추어 super 호출
super(name, size);
}
@Override
public void sound() {
System.out.println("멍멍");
}
}
Box<T> : 객체 보관용 제네릭 클래스
package generic.ex2;
// 객체를 보관할 수 있는 제네릭 클래스
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
Main 코드
package generic.ex2;
import generic.animal.Animal;
import generic.animal.Cat;
import generic.animal.Dog;
public class AnimalMain1 {
public static void main(String[] args) {
// 동물, 개, 고양이 정의
Animal animal = new Animal("동물", 0);
Dog dog = new Dog("멍멍이", 100);
Cat cat = new Cat("냐옹이", 50);
Box<Dog> dogBox = new Box<>();
dogBox.set(dog);
Dog findDog = dogBox.get();
System.out.println("findDog : " + findDog);
Box<Cat> catBox = new Box<>();
catBox.set(cat);
Cat findCat = catBox.get();
System.out.println("findCat : " + findCat);
Box<Animal> animalBox = new Box<>();
animalBox.set(animal);
Animal findAnimal = animalBox.get();
System.out.println("findAnimal : " + findAnimal);
}
}
// 결과
findDog : Animal{name='멍멍이', size=100}
findCat : Animal{name='냐옹이', size=50}
findAnimal : Animal{name='동물', size=0}
- Box<T> : 제네릭 클래스이며 각 타입에 맞는 동물을 보관하는 용도
- Box<Dog> dogBox = Dog 타입을 보관
- ..
6. 문제
6.1. 제네릭 기본 #1
다음 코드와 실행 결과를 참고해서 Container 클래스를 만들어라. Container 클래스는 제네릭을 사용해야 한다.
package generic.test.ex1;
public class ContainerTest {
public static void main(String[] args) {
Container<String> stringContainer = new Container<>();
System.out.println("빈값 확인1: " + stringContainer.isEmpty());
stringContainer.setItem("data1");
System.out.println("저장 데이터: " + stringContainer.getItem());
System.out.println("빈값 확인2: " + stringContainer.isEmpty());
Container<Integer> integerContainer = new Container<>();
integerContainer.setItem(10);
System.out.println("저장 데이터: " + integerContainer.getItem());
}
}
정답
package generic.test.ex1;
public class Container<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public Boolean isEmpty() {
return item == null;
}
}
반응형
'Programming > Java' 카테고리의 다른 글
[Java] 김영한의 자바 중급 2편 #3 - ArrayList (1) | 2024.11.10 |
---|---|
[Java] 김영한의 자바 중급 2편 #2 - 제네릭2 (1) | 2024.11.09 |
[Java] 김영한의 자바 중급 1편 #10 - 예외처리 실습 (0) | 2024.10.15 |
[Java] 김영한의 자바 중급 1편 #9 - 예외처리 이론 (0) | 2024.10.15 |
[Java] 김영한의 자바 중급 1편 #8 - 중첩 클래스, 내부 클래스 - 2 (0) | 2024.10.04 |