2023_1_19_TIL
제네릭 없이 여러 객체를 저장하는 클래스 작성하기
class Apple {}
class Goods1 {
private Apple apple = new Apple();
public Apple getApple() {
return apple;
}
public void setApple(Apple apple) {
this.apple = apple;
}
}
class Pencil {}
class Goods2 {
private Pencil pencil = new Pencil();
public Pencil getPencil() {
return pencil;
}
public void setPencil(Pencil pencil) {
this.pencil = pencil;
}
}
public class ProblemBeforeGeneric {
public static void main(String[] args) {
Goods1 goods1 = new Goods1();
goods1.setApple(new Apple());
Apple apple = goods1.getApple();
Goods2 goods2 = new Goods2();
goods2.setPencil(new Pencil());
Pencil pencil = goods2.getPencil();
}
}
class Apple{}
class Pencil{}
class Goods {
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
public class Solution_UsingObject {
public static void main(String[] args) {
Goods goods1 = new Goods();
goods1.setObject(new Apple());
Apple apple = (Apple) goods1.getObject();
Goods goods2 = new Goods();
goods2.setObject(new Pencil());
Pencil pencil = (Pencil) goods2.getObject();
}
}
제네릭 클래스와 제네릭 인터페이스 정의하기
- 잘못된 캐스팅? -> 바로 오류 -> 강한 타입 체크
제네릭 클래스의 객체 생성
- 제네릭 클래스 -> 객체 생성과정은 일반 클래스의 객체 생성과정과 비슷
- 단 객체를 생성할 때, 제네릭 타입 변수에 실제 타입을 대입하는 것
- 즉, 제네릭 클래스는 클래스를 정의하는 시점에 타입을 지정하는 것이 아니라 객체를 생성하는 시점에 타입을 지정 -> 하나의 제네릭 클래스로 다양한 타입의 객체를 저장 및 관리 할 수있는 객체를 생성할 수 있는 것
- Object 처럼 다운 캐스팅 필요없음
class MyClass<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public class SingleGenericArgument {
public static void main(String[] args) {
MyClass<String> mc1 = new MyClass<>();
mc1.setT("안녕");
System.out.println(mc1.getT());
MyClass<Integer> mc2 = new MyClass<>();
mc2.setT(123123123);
System.out.println(mc2.getT());
}
}
class KeyValue<K, V> {
private K key;
private V value;
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
public class TwoGenericArguments {
public static void main(String[] args) {
KeyValue<String, Integer> kv1 = new KeyValue<>();
kv1.setKey("사과");
kv1.setValue(1000);
String key1 = kv1.getKey();
int value1 = kv1.getValue();
System.out.println("key: " + key1 + " value: " + value1);
KeyValue<Integer, String> kv2 = new KeyValue<>();
kv2.setKey(404);
kv2.setValue("Not Found(요청한 페이지를 찾을 수 없습니다.)");
int key2 = kv2.getKey();
String value2 = kv2.getValue();
System.out.println("key: " + key2 + " value: " + value2);
KeyValue<String, Void> kv3 = new KeyValue<>();
kv3.setKey("키 값만 사용");
String key3 = kv3.getKey();
System.out.println("key: " + key3);
}
}
class Apple{}
class Pencil{}
class Good<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public class Solution2_Generic {
public static void main(String[] args) {
Good<Apple> good1 = new Good<>();
good1.setT(new Apple());
Apple apple = good1.getT();
Good<Pencil> good2 = new Good<>();
good2.setT(new Pencil());
Pencil pencil = good2.getT();
Good<Apple> good3 = new Good<>();
good3.setT(new Apple());
}
}
제네릭 메소드의 정의와 호출
- 클래스 전체를 제네릭으로 선언 대신, 일반 클래스 내부에 특정 메소드만 제네릭 선언 가능
- 제네릭 메소드는 호출되는 시점에 실제 제네릭 타입을 지정
- 입력매개변수를 보고 제네릭 타입 변수의 실제 타입을 예측 가능
class GenericMethods {
public <T> T method1(T t) {
return t;
}
public <T> boolean method2(T t1, T t2) {
return t1.equals(t2);
}
public <K, V> void method3(K k, V v) {
System.out.println(k + " " + v);
}
}
public class GenericMethod {
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
String str1 = gm.method1("안녕");
String str2 = gm.method1("안녕");
System.out.println(str1);
System.out.println(str2);
boolean boo11 = gm.method2(2.5, 2.5);
boolean bool2 = gm.method2(2.5, 2.5);
System.out.println(boo11);
System.out.println(bool2);
gm.method3("국어", 86);
}
}
제네릭 메소드 내에서 사용할 수 있는 메소드
- 제네릭 메소드를 정의하는 시점에서는 아직 어떤 타입이 입력될지 모름
- 특정 타입에 포함되어 있는 메소드는 메소드를 정의하는 시점에 사용할 수 없다
- 아직 결정되지 않은 제네릭 타입 T객체 내부에는 어떤 메소드 가능?
- Object 타입의 메소드만 가능 -> 나중에 어떤 타입이 제네릭 타입 변수로 확정되어도, 항상 사용할 수 있는 메소드만 제네릭 메소드 내부에서 사용가능
- 즉, 어떤 상황이든 제네릭 타입은 입력매개변수 객체 내부에서는 Object에게 상속받은 메소드만 활용 가능!
class A {
public <T> void method(T t) {
System.out.println(t.equals("안녕"));
}
}
public class AvailableMethodInGenericMethod {
public static void main(String[] args) {
A a = new A();
a.method("안녕");
}
}
제네릭 타입 범위 제한의 필요성
- 제네릭 타입은 각각 객체를 생성할 때와 메소드를 호출할 때 제네릭 타입을 지정함 -> 다양한 타입 처리 가능
- 특정 부분만 관리하는 클래스 생성하고 싶다면? -> 제네릭 타입으로 올 수 있는 실제 타입의 종류를 제한해야 함 -> 제네릭 타입의 범위 제한
제네릭 타입 범위 제한의 종류와 범위 제한 방법
- 제네릭 클래스의 타입 제한
- 접근지정자 class 클래스명<T extends 최상위클래스/인터페이스>
- 제네릭 클래스의 객체를 생성할 때 제네릭 타입을 생략? -> 모든 타입의 최상위 클래스가 입력
class A{}
class B extends A {}
class C extends B {}
class D <T extends B> {
private T t;
public T get() {
return t;
}
public void set(T t) {
this.t = t;
}
}
public class BoundedTypeOfGenericClass {
public static void main(String[] args) {
D<B> d2 = new D<>();
D<C> d3 = new D<>();
D d4 = new D();
d2.set(new B());
d2.set(new C());
d3.set(new C());
d4.set(new B());
d4.set(new C());
}
}
- 제네릭 메소드의 타입 제한
- 접근지정자 <T extends 최상위클래스/인터페이스명> T 메소드명(T t)
- 타입을 제한하지 않을 때는 모든 탕비의 최상위 클래스 Object만 가능
- 예시) <T extends String클래스> -> 최상위 타입 String객체 메소드 사용 가능
class A {
public <T extends Number> void method1(T t) {
System.out.println(t.intValue());
}
}
interface MyInterface {
public abstract void print();
}
class B {
public <T extends MyInterface> void method1(T t) {
t.print();
}
}
public class BoundedTypeOfGenericMethod {
public static void main(String[] args) {
A a = new A();
a.method1(5.8);
B b = new B();
b.method1(new MyInterface() {
@Override
public void print() {
System.out.println("print() 구현");
}
});
}
}
- 메소드 매개변수일 때 제네릭 클래스의 타입 제한
class A{}
class B extends A{}
class C extends B{}
class D extends C{}
class Goods<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
class Test {
void method1(Goods<A> g){}
void method2(Goods<?> g){}
void method3(Goods<? extends B> g){}
void method4(Goods<? super B> g){}
}
public class BoundedTypeOfInputArguments {
public static void main(String[] args) {
Test t = new Test();
t.method1(new Goods<A>());
t.method2(new Goods<A>());
t.method2(new Goods<B>());
t.method2(new Goods<C>());
t.method2(new Goods<D>());
t.method3(new Goods<B>());
t.method3(new Goods<C>());
t.method3(new Goods<D>());
t.method4(new Goods<B>());
t.method4(new Goods<A>());
}
}
제네릭 클래스의 상속
- 부모클래스가 제네릭? -> 상속받은 자식클래스도 제네릭
- 제네릭 타입 변수 전부 물려받음
- 자식클래스 제네릭 타입 변수의 개수 -> 같거나 많거나
class Parent<T> {
T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
class Child1<T> extends Parent<T> {}
class Child2<T, V> extends Parent<T> {
V v;
public V getV() {
return v;
}
public void setV(V v) {
this.v = v;
}
}
public class InheritanceGenericClass {
public static void main(String[] args) {
Parent<String> p = new Parent<>();
p.setT("부모 제네릭 클래스");
System.out.println(p.getT());
Child1<String> c1 = new Child1<>();
c1.setT("자식1 제네릭 클래스");
System.out.println(c1.getT());
Child2<String, Integer> c2 = new Child2<>();
c2.setT("자식2 제네릭 클래스");
c2.setV(100);
System.out.println(c2.getV() + c2.getT());
}
}
제네릭 메소드의 상속
- 제네릭 메소드 또한 제네릭 클래스와 마찬가지(그대로 물려받는다!)
class Parent {
<T extends Number> void print(T t) {
System.out.println(t);
}
}
class Child extends Parent{}
public class InheritanceGenericMethod {
public static void main(String[] args) {
Parent p = new Parent();
p.print(10);
Child c = new Child();
c.print(304313);
}
}