제너릭 (Generics)
1. Generic Type Reference
- Generic : 클래스, 인터페이스, 메서드를 좀더 일반적인 상황에서 활용하고 재사용성을 높이는 방법.
- 다양한 자료형에 대해 동일한 동작을 할 수 있는 방법을 제공.
ex. 정수형 배열을 속성으로 가지고, 그 최댓값과 평균을 관리하는 클래스가 있다면..?
똑같은 기능을 실수로 만들고 싶다! > 그러면 또 똑같은 형태의 클래스를 만들거나 클래스 내부에 double[]을 담는 속성/메서드 모두 구현해야한다.... ㅠㅡㅠ
public class IntArrayWrapper {
private final int[] intArray;
public IntArrayWrapper(int[] intArray) {
this.intArray = intArray;
}
// 1. intArray의 길이를 반환하는 메서드
public int length() {
return this.intArray.length;
}
// 2. 주어진 int target이 intArray에 존재하는지 반환하는 메서드
public boolean contains(int target) {
for (int number : this.intArray) {
if (target == number) return true;
}
return false;
}
// 3. intArray의 원소 중 최댓값
public int max() {
int max = this.intArray[0];
for (int number : this.intArray) {
// Math: 여러 수학 작업이 존재하는 클래스
max = Math.max(number, max);
}
return max;
}
}
이때 Generic을 활용하자!
How ?
>> 클래스 이름 옆에 <T>를 넣어보자.
T: Type Parameter 클래스를 매개변수로 받게 해준다 / T를 타입처럼 쓸 수 있다.
- Comparable를 내부에 구현 가능
- 여러 클래스나 인터페이스를 &로 연결 가능
public class ArrayWrapper<T extends Comparables<T>> {
// extends Comparable<T>를 쓰면 비교가 가능한 자료형만으로 제한 가능
private final T[] array;
public ArrayWrapper(T[] array) {
if (array == null)
throw new IllegalArgumentException("null array provided");
this.array = array;
}
// 1. intArray의 길이를 반환하는 메서드
public int length() {
return array.length;
}
// 2. 주어진 int target이 intArray에 존재하는지 반환하는 메서드
public boolean contains(T target) {
for (T item : array) {
if (item == null) continue;
if (item.equals(target)) return true;
}
return false;
}
// 3. intArray의 원소 중 최댓값
public T max() {
T max = this.array[0];
for (T item : this.array) {
if (item==null) continue;
if (max==null) max = item;
// Math: 여러 수학 작업이 존재하는 클래스
// 비교가 가능한 것만 구현해야 한다.
else if (item.compareTo(max)>0) max = item;
}
return max;
}
}
2. Optional
: null이 나올 수 있는 결과를 Optional로 덮어쓸 수 있다.
public static String nullOrString() {
Random random = new Random();
if (random.nextInt() % 2 == 0)
return "not null string";
else return null;
} // 랜덤값은 null을 반환할 수도 있다.
- 이 때 Optional을 활용할 수 있다.
isPresent : null이 아닌 값이 들어올 때 true
isEmpty : null일 때 true
Optional<String> optionalString = Optional.ofNullable(nullOrString());
//어떤 메서드든 Optional.ofNullable()메서드를 통해 결과 반환하게 한다.
if (optionalString.isPresent()) { // 결과가 존재하면 실행될 코드
// get으로 실제 값을 반환받아 사용
System.out.println(optionalString.get());
} else { // 결과가 존재하지 않으면 실행될 코드
System.out.println("got: null");
}
- .orElse(T obj)로 null일때 기본값을 활용할 수도 있다.
String resultString = optionalString.orElse("got: null");
System.out.println(resusltString);
// 값이 존재하면 존재하는 값을, 없으면 기본값을 반환
- 아예 메서드가 Optional을 반환하게 만들 수도 있다.
public static Optional<String> nullOrStringOpt() {
Random random = new Random();
// Optional.of로 Optional을 만든다.
if (random.nextInt() %2 == 0) return Optional.of("concrete string");
// null결과는 Optional.empty()
else return Optional.empty();
}
3. Collections Framework
: 자바에서 만들어준 데이터 정리용 클래스.- 인터페이스를 활용해 사용방식을 통일하고, 구현 클래스를 통해 실제 자료구조를 달리 할 수 있게 하는 도구 모음집.
1) List<E>
: 순서가 있는 데이터를 나타내는 인터페이스
- 중복 허용
- 원소를 일렬로 세워 추가 제거 조회 등을 위해 많이 활용
- index 방식으로 가져올 수 있음.
- ArrayList : 배열을 관리해줌. 동적으로 배열의 크기를 바꿔가며 활용, 임의의 위치에서 원소 가져오기 좋음
- LinkedList : 연결 리스트 형태. 데이터를 관리, 원소를 추가 제거하는데 유리
boolean add(E element) | element를 List의 끝에 넣어준다. 성공하면 true 반환 |
void add(int index, E element) | element를 index의 위치에 넣어주고 원래 원소들을 뒤로 밀어준다 |
E get(int index) | index에 있는 원소 반환 |
int indexOf(Object o) | o가 있으면 그 index를, 없으면 -1을 반환 |
E remove(int index) | index에 있는 원소 제거하고 반환, 뒤의 남은 원소들을 앞으로 당겨줌 |
boolean remove(Object o) | o가 있다면 제거 |
2) Set<E>
: 순서 보장하지 않는 원소 집합을 나타내는 인터페이스
- 중복 허용 X
- 데이터 모음 중 중복된 데이터를 제거하거나 어떤 원소가 있는지 없는지를 확인하는데 유용
- HashSet : 객체를 hashCode로 구분한다. (hashSet 쓸 때 hashCode() 오버라이딩 필요)
boolean add(E element) | element를 추가. 이미 있는 데이터면 false 반환. |
boolean addAll(Collection<? extends E> c) | 또다른 Collection 인터페이스 원소들 중 없는 원소 추가. 추가한 원소가 하나라도 존재하면 true 반환 |
boolean remove(Object o) | o가 있다면 제거. 제거했다면 true 반환 |
boolean removeAll(Collection<? extends E> c) | 또다른 Collection 인터페이스 원소들 중 있는 원소 제거. 제거한 원소가 존재하면 true 반환 |
void clear() | Set을 비움. |
Set<String> skillSet = new HashSet<>();
skillSet.add("java");
skillSet.add("kotlin");
skillSet.add("java"); //중복 원소는 들어가지 않음.
List<String> skillList = new ArrayList<>();
skillList.add("java");
skillList.add("git");
skillList.add("md");
skillSet.addAll(skillList); //true
System.out.println(skillSet); // 결과값은 java, kotlin, git, md (중복 제거)
skillSet.removeAll(skillList); //true
System.out.println(skillSet); // 결과값은 java, kotlin (skillSet에서 skillList빼기)
3) Iterable
: List와 Set의 기초가 되는 인터페이스
- 반복 가능하다는 뜻! foreach로 순회가 가능.
cf) foreach문 사용중 대상 리스트가 변경/제거되면 ConcurrentModificationException이 발생할 수 있음.
- 추상 메서드 iterator가 정의되어 있고, 구현 클래스의 원소들을 검사하는 Iterator 객체를 돌려주는 메서드.
- Iterator라는 객체를 활용하여 Iterable이 담고 있는 데이터를 하나씩 확인할 수 있다.
// Iterator
List<String> skillList = new ArrayList<>();
Iterator<String> iter = skillList.iterator();
while (iter.hasNext()) { // 아직 남은 원소가 있는지 판단
String element = iter.next(); // 다음 원소를 가져온다. 없으면 오류
if (element.equals("python") iter.remove(); // python이 제거된다.
}
System.out.println(skillList);
// foreach 구문으로 사용
for (String skill: skillList) {
System.out.println(skill);
skillList.remove("python"); //오류남 (ConcurrentModificationException)
}
boolean hasNext() | 다음 원소가 존재하는지 여부를 반환 |
T next() | 호출할 때마다 다음 차례의 원소를 반환 |
void remove() | 현재 위치의 원소를 제거. next()를 사용한 다음 사용해야 하며, next()로 한 번 확인할 때마다 한번만 사용 가능. |
4) Collection
: Iterable을 상속받으며 List와 Set의 실제 동작이 정의된 인터페이스.
- 표준 생성자는 아무것도 받지 않는 생성자와, Collection을 받아 그 원소들을 다 포함하는 Collection을 만드는 생성자.
- 즉, 집합을 쓰다가 리스트로, 리스트를 쓰다가 집합으로 변환 가능.
int size() | Collection의 원소의 갯수 반환 |
boolean isEmpty() | Collection이 비어있는지 여부를 반환 |
boolean contains(Object o) | Collection에 o가 있는지 반환 |
Set<String> skillSet = new HashSet<>();
List<String> listFromSet = new ArrayList<>(skillSet);
5) Map<K, V>
: key와 value쌍으로 이루어진 인터페이스.
- 데이터와 그 데이터를 찾기 위한 용도의 키를 함께 저장. > 데이터의 값이 필요할 때 키를 바탕으로 찾음.
- put(k, v)으로 키와 값을 동시에 전달
- get(k)으로 키로 값을 가지고 옴.
- Collection 인터페이스 구현하지 않음!!
- HashMap : 키를 hashCode로 구분.
V put (K key, V value) | Key에 Value를 할당. 이미 존재하는 Key면 이전 Value를 반환 |
V get(Object key) | key에 해당하는 값을 반환하거나 null 반환 |
boolean containsKey(Object key) | key와 일치하는 key가 맵에 존재하면 true 반환 |
boolean containsValue(Object value) | value와 일치하는 value가 맵에 하나라도 존재하면 true 반환 |
V remove(Object key) | key에 해당하는 값을 제거하고 반환. 없으면 null 반환 |
Set<K> keySet() | 모든 key로 구성된 Set을 반환 |
Set<Map.Entry<K, V>> entrySet() | 맵의 key, value 쌍으로 구성된 Entry 객체의 Set 반환 |
- keySet(), entrySet()을 활용하면 맵의 모든 키와 값들을 순회할 수 있다.
>> Map.Entry형 객체가 담긴 집합을 반환. (getKey(), getValue() 사용)
Map<String, String> gitConfig = new HashMap<>();
gitConfig.put("user.email", "email@gmail.com");
gitConfig.put("user.name", "ys");
for(String key: gitConfig.keySet()) {
System.out.println(String.format("key: %s", key));
System.out.println(String.format("value: %s", gitConfig.get(key)));
}
for (Map.Entry<String, String> entry : gitConfig.entrySet()) {
System.out.println(String.format("key: %s", entry.getKey()));
System.out.println(String.format("value: %s", entry.getValue()));
}
'Programming > Java' 카테고리의 다른 글
File I/O (1) | 2023.12.03 |
---|---|
Stream API (1) | 2023.11.30 |
예외처리 (Exception Handling) (1) | 2023.11.28 |
객체지향 프로그래밍 (1) | 2023.11.28 |
클래스 (Class) (1) | 2023.11.28 |