1. Generic 알아보기
2. Generic 문법 자세히 살펴보기
1. Generic 알아보기
제네릭의 효용
- 첫 번째로 제네릭의 효용은 타입 언어에서 “중복되거나 필요 없는 코드를 줄여주는 것”이다.
- 두 번째 제네릭의 효용은 그러면서도 타입 안정성을 해치지 않는 것이다
- Step 1. 타입 언어에서의 중복되거나 필요 없는 코드?
- 만약 우리가 자바스크립트나 파이썬과 같은 약타입 언어를 이용한다면 일은 간단하다.
- 기본적으로 타입을 지정해 줄 필요가 없기에 하나의 함수만 구현하면 끝.
// JavaScript
function plusReturnFunction(a, b) {
return a + b;
}
const a = 1;
const b = 2;
const c = 1.1;
const d = "hello";
plusReturnFunction(a + b); // 3
plusReturnFunction(a + c); // 2.1
plusReturnFunction(a + d); // 1hello
- 하지만 우리는 타입을 지정해줘야 하는 언어를 사용한다.
- 그래서 똑같은 로직을 수행하는 함수를 타입을 지정해야 한다는 이유로 세 차례나 구현해야 한다.
public class Generic {
public String plusReturnFunction(int a, int b) { ... }
public String plusReturnFunction(int a, long b) { ... }
public String plusReturnFunction(int a, String b) { ... }
}
- 타입의 안정성을 해치지 않으면서 로직을 수행할 수 있는 함수를 생각하면 이런 식 일 것이다
public class Generic {
public Object plusReturnFunction(Object a,Object b) { ... }
}
- 자바의 “거의 모든 것”은 객체이고, 객체라는 것은 Object 클래스를 상속한다.
- Object를 상속받기 때문에 위의 코드와 같이 작성을 한다면, (Wrapper 객체에 대한 설명이 조금은 필요하겠지만) 실제로 여러분은 타입과 상관없이 메서드 안에 두 파라미터를 전달하는 것은 가능할 것이다.
- 하지만 이런 경우 타입 안정성이 침해받게 된다.
- 직접 메서드를 위와 같이 구현했기 때문에, 지금은 아주 작아 보이는 { … } 블록 안에 모든 경우의 수를 대비해야 한다.
- 또한 우리가 구현한 메서드 내부는 아직도 타입에 지배받고 있다.
- 결론적으로는 다음과 같은 문제들이 발생.
- 예를 들어 a 객체와 b 객체에 단항 연산자를 사용할 수 있을까?
- 또는 두 타입이 다르다면 연산자를 사용하기 위해 같은 타입으로 맞춰줘야 하는데, 어떠한 타입으로 맞출 수 있을까? int? long?
- 마지막으로 순서는 어떻게 할까? int long을 형변환 해서 처리하는 로직의 코드를 작성했는데, long과 int로 들어오면 어떻게 할까?
- 타입의 논리로 동작하는 세상에서, 타입 안정성을 침해하는 행위를 했다. 그러한 댓가로 우리는 잘 피해간 것 처럼 보였지만, 형변환과 같은 부수적인 코드는 오히려 늘어났다.
제네릭 문법 살펴보기
// 1.
public class Generic<T> {
// 2.
private T t;
// 3.
public T get() {
return this.t;
}
public void set(T t) {
this.t = t;
}
public static void main(String[] args) {
// 4.
Generic<String> stringGeneric = new Generic<>();
// 5.
stringGeneric.set("Hello World");
String tValueTurnOutWithString = stringGeneric.get();
System.out.println(tValueTurnOutWithString);
}
}
- 제네릭은 클래스 또는 메서드에 사용 할 수 있습니다. 클래스 이름 뒤에 <> 문법 안에 들어가야 할 타입 변수를 지정한다. 대부분 T, U, V, E 순으로 사용
- 선언해둔 타입 변수는 해당 클래스 내에서 특정한 타입이 들어갈 자리에 대신 들어갈 수 있다.
- 메서드의 리턴타입에 들어가는 것 역시 마찬가지이다.
- 여기부터는 제네릭을 통해 구현한 클래스를 사용하는 부분입니다, 클래스에 선언했기 때문에 인스턴스를 만들기 위해서 타입변수에 들어갈 실제 변수의 값을 넣어줘야 한다. 여기서는 String이다.
- 아까 타입변수로 대체해뒀던 곳에 String이 들어가 있기 때문에, 이와 같이 사용할 수 있다.
2. Generic 문법 자세히 살펴보기
제네릭 용어
public class Generic<T> { ... }
Generic<String> stringGeneric = new Generic<>();
- Generic <T>의 클래스처럼, 제네릭을 사용한 클래스를 제네릭 클래스라고 한다.
- 제네릭에서 <> 사이에 들어가는 변수명 T는 타입 변수라고 한다.
- Generic 클래스를 원시 타입이라고 한다.
제네릭 제한
1. 객체의 static 멤버에 사용할 수 없다.
static T get() { ... } // 에러
static void set(T t) { ... } // 에러
- 타입 변수는 인스턴스 변수로 간주되고, 모든 객체에 동일하게 동작해야 하는 static 필드 특성상 사용할 수 없다.
2. 제네릭 배열을 생성할 수 없습니다.
제네릭의 문법
1. 다수의 타입변수를 사용할 수 있습니다.
public class Generic<T, U, E> {
public E multiTypeMethod(T t, U u) { ... }
}
Generic<Long, Integer, String> instance = new Generic();
instance.multiTypeMethod(longVal, intVal);
2. 다형성 즉 상속과 타입의 관계는 그대로 적용
- 대표적으로 부모 클래스로 제네릭 타입변수를 지정하고, 그 안에 자식클래스를 넘기는 것은 잘 동작한다.
3. 와일드카드를 통해 제네릭의 제한을 구체적으로 정할 수 있다.
public class ParkingLot<T extends Car> { ... }
ParkingLot<BMW> bmwParkingLot = new ParkingLot();
ParkingLot<Iphone> iphoneParkingLot = new ParkingLog(); // error!
- <? extends T> : T와 그 자손들만 사용 가능
- <? super T> : T와 그 조상들만 가능
- <?> : 제한 없음
이렇게 제한을 하는 이유는 다형성 때문. 위의 코드에서, T는 Car의 자손클래스들이라고 정의했기 때문에, 해당 클래스 내부에서 최소 Car 객체에 멤버를 접근하는 코드를 적을 수 있다. 반대로 그러한 코드들이 있을 여지가 있기 때문에, Car 객체의 자손이 아닌 클래스는 제한하는 것이다.
4. 메서드를 스코프로 제네릭을 별도로 선언할 수 있다.
static <T> void sort(List<T> list, Comparator<? super T> c) { ... }
- 이렇게 반환 타입 앞에 <> 제네릭을 사용한 경우, 해당 메서드에만 적용되는 제네릭 타입변수를 선언 할 수 있다.
- 타입변수를 클래스 내부의 인스턴스 변수 취급하기 때문에 제네릭 클래스의 타입변수를 static 메서드에는 사용할 수 없었지만, 제네릭 메서드의 제네릭 타입 변수는 해당 메소드에만 적용되기 때문에 메소드 하나를 기준으로 선언하고 사용 할 수 있다.
- 같은 이름의 변수를 사용했다고 해도 제네릭 메소드의 타입변수는 제네릭 클래스의 타입변수와 다르다.
public class Generic<T, U, E> {
// Generic<T,U,E> 의 T와 아래의 T는 이름만 같을뿐 다른 변수
static <T> void sort(List<T> list, Comparator<? super T> c) { ... }
}
오늘 자바 제네릭을 공부하며 제네릭의 효용과 문법 여러 제한사항과 어떤 상황에서 사용하는지에 알아봤다. 자바를 공부하면서 제네릭 같은 문법하나 있으면 좋겠다고 했지만. 막상 사용하려고 보니 여러 제한 조건과 문법이 내 발목을 잡을 것 같다ㅎㅎ... 그래도 자주 사용할 문법이니 눈에 불이 나게 익혀보자.
'배운내용 정리' 카테고리의 다른 글
Java Thread(2) (0) | 2023.10.27 |
---|---|
Java Thread(1) (0) | 2023.10.24 |
Java 예외처리 (1) | 2023.10.23 |
자바의 구조체 (0) | 2023.10.18 |
객체지향(3) (1) | 2023.10.17 |