[Java] 범용 프로그래밍

이현준·2020년 12월 1일
5

Java

목록 보기
14/15

범용 프로그래밍

범용 프로그래밍이란 하나의 코드를 이용하여 다양한 타입을 처리하는 것을 말한다. 이렇게 하면 프로그래머가 코딩해야 하는 양을 줄일 수 있으며, 코드 중복을 없애는 또 하나의 방법이다. 범용 프로그래밍은 크게 범용 함수를 정의하는 것과 범용 클래스를 정의하는 것으로 구분될 수 있다. 함수는 어떤 기능을 수행하는 것이기 때문에 범용 함수는 해당 기능을 수행할 수 있는 타입만 받을 수 있도록 제한할 수 있어야 강건한 프로그래밍이 된다. 이를 위해 자바에서는 상속이나 interface를 이용하여 특정 메소드를 가지고 있는 객체만 인자로 받을 수 있도록 제한할 수 있다. 범용 클래스는 다양한 타입의 값을 멤버로 유지하는 것이 목적이기 때문에 범용 함수와 달리 interface를 이용하여 저장할 타입을 보통 제한하지 않는다. 특히, 자료구조를 구현할 경우에는 모든 타입을 유지할 수 있어야 한다.

범용 메소드

타입변수를 가지는 메소드를 정의하는 것
(매개 변수로 다양한 타입을 받을 수 있다.)
예) void foo(Pet p); void bar(Flyable f); 이런 것도 범용 함수이지만 여기서는 template기능을 사용해서 범용함수를 만들어볼 예정임

  • 함수는 어떤 기능을 수행하는 것이 목적: 해당 기능을 할 수 있는 타입만 인자로 받을 수 있도록 제한되어야 함. 예) void sort(Comparable[] arr);
  • 보통 상속이나 interface를 이용하여 해당 함수에서 필요로 하는 메소드를 가진 객체들만 처리하도록 프로그래밍함
    • 예) void foo(Pet p); void bar(Flyable f);

범용 클래스

  • 범용 클래스는 다양한 타입의 값을 유지하는 것이 목적이기 때문에 보통 interface 등을 이용하여 유지하는 것을 제한하지 않음
  • 특히, 자료구조를 정의하는 것이면 모든 타입을 유지할 수 있어야 함
  • 자바 5 이전에는 Object 타입을 이용함(타입 변환을 해줘야 되는 등 문제점이 좀 있음)
  • 자바 5 이후에는 template 기능을 이용함
    • 프로그래머가 직접 타입 변환하지 않고 편리하게 프로그래밍 가능
    • 엉뚱한 타입이 인자로 전달되는 것을 제한할 수 있음

단 범용함수하고 범용 클래스하고 다른데 범용 클래스는 모든 타입의 값을 유지하는 게 목표(ex) 범용 스택)이지만 범용 함수인 경우 모든 타입을 받겠다는 것은 아님 함수는 어떤 기능을 수행하는 것이 목적이기 때문에 해당 기능을 할 수 있는 타입만 받아야 함

Java 5.0 이전 범용 프로그래밍의 문제점

보통 interface와 object를 통해 범용 클래스를 구현했다.


문제점 1. 매번 타입 변환이 필요함
문제점 2. 오류 검사가 가능하지 않음
나는 String만 넣고 싶은데 Integer를 넣을 수 있음
문제점 3. 오류 검사가 가능하지 않기 때문에 Object를 이용한 복합타입에 서로 다른 타입의 객체를 유지할 수 있음

Java 5.0부터 제공된 타입 매개변수(type parameter)라는 것을 이용하여 3개의 문제점을 모두 해결


여기서는 String만 들어올 수 있으므로 타입변환을 해주지 않아도 되고 넣을때도 타입에 맞춰서 들어가야하기 때문에 Int를 넣으려 하면 에러가 뜨게 된다.

주의점! 타입 매개변수로 원시타입 자체는 불가능함 ex) ArrayList<int> X 쓰려면 wrapper클래스 활용

범용 프로그래밍을 프로그래머 입장에서 봤을 때

범용 클래스

타입 변수 T를 메소드의 매개변수나 반환 타입으로 사용할 수 있다.
여기엔 없지만 메소드 내부의 멤버변수로도 사용 가능


하지만 지금 같은 경우에는 오른쪽 아래 박스가 좀 더 괜찮은 방법이라고 함

범용 객체의 생성

앞에 var 로 정의되어있으면 뒤의 타입 지정은 생략할 수 없다.(3번째 박스)

범용 메소드


보통 특정 객체의 메소드가 아니기 때문에 static 메소드로 많이 정의한다.
또한 위의 같이 모든 타입을 받는 범용 메소드는 잘 사용하진 않는다.(메소드가 특정 기능을 하기위해 필요한 타입들을 받으려하기 때문에) 그래서 타입변수를 써도 interface로 제한을 걸어준다.

생성할때는 <> 이거는 절대 생략하면 안됐는데 사용할 때는 <>를 생략할 수도 있다.

타입 변수의 제한


여기서는 compareTo를 사용할 수 있는 매개변수가 와야된다!
제한하기 위해 extends를 사용한다. extends하고 보통 interface를 써준다. (여기서 만약 interface도 범용이면 위 코드의 아래박스처럼 Comparable<T>와 같이 타입 변수도 같이 명시해준다.)


위의 Pair를 아래에서 범용 메서드로 만들어준다.

,가 아닌 &로 타입 변수에 다중 제한을 걸어줄 수 있다. 이때 T extends A & B & C와 같은 형태로 한다고 했을 때 interface뿐 아니라 클래스도 올 수 있는데 클래스는 단 한개만 그것도 맨 앞에(A자리) 써줘야한다.


Pair<shapePair<Rectangle>은 상속관계가 아니라 같은 클래스임. => 단 이러면 상속관계를 활용할 수 없게된다. 그래서 Wildcard를 사용해준다.

<? extends Person> : Person과 Person을 상속하는 애들이 올 수 있다.
<? super Student> : Student와 Student의 부모만 올 수 있음

<?> : 이건 <? extends object>가 축약된 형태로서 모든 객체가 올 수 있다는 것 뜻함

왼쪽 아래와 같이 ? 로는 못 받는다. 구현하고 싶으면 별도로 오른쪽 아래의 2개의 박스와 같이 구현해서 가능하다.

타입 변수의 전달

처음에 쓴 T 여기선 첫줄에 LinkedList <T> 인데 이 T 랑 똑같은 문자로 implements Iterable<T> 같이 다른 곳에서도 쓰면 처음에 쓴 T가 이렇게 쓰는 곳들에 다 전달이 된다.
근데 만약 처음에 쓴 문자와 다른 문자를 쓴다면 예를 들어 implements Iterable<U>와 같이 쓰면 이 U를 정의, 전달할 방법이 없다. 그래서 처음에 정의한 문자로 나머지들을 정의해줘야 한다.

static 내부 클래스는 독립적인 클래스이므로 연결을 해줘도되고 안해줘도 되지만 (implements Iterable<T> 이런 경우는 public class LinkedList<T> 여기서 정의해준 타입변수와 무조건 연결해줘야 하지만) 연결을 해주려면 static 클래스도 범용 클래스로 하고 같은 문자로 꼭 해줘야된다. static인 아닌 위의 경우

public class LinkedList<T> implements Iterable<T>{
private class Node{
private T item;
private Node next;
}
private class ListIterator implements Iterator<T>{
}public void add(T item){
}
} 

는 T가 바로 연결되지만 static 위와 같이 범용클래스로 만들기 같은 문자로 써야 연결이 되기 때문에 주의해야된다

그리고 linkedlist에서 만약 node 내부 클래스를 staic 클래스로 하지 않으면 외부 객체(클래스)를 가리키는 포인터를 가지게 된다. 하지만 이는 연결구조에서 필요가 없기 때문에 그래서 static 클래스가 되어야 한다.

static 메소드 또한 static 내부 클래스 처럼 독립적이기 때문에 아래 박스와 같은 방식으로 설정 해줘야 한다.
각각 마다 <T extends Comparable<T>> T min(T... list){} 이런식으로..

범용 코드와 JVM

"template code bloat" : 너무 많이 생성될 수 있다.

주의사항



?를 이용해서 Pair타입인지는 확인할 수 있지만 이게 String으로 만들어진건지 Integer로 만들어진건지는 알 수 없다.

예제

public class Pair<U, V>{
	private U first = null;
	private V second = null;
	public Pair(U first, V second) {
		setFirst(first);
		setSecond(second);
	}
	public U getFirst() { return first; }
	public V getSecond() { return second; }
	public void setFirst(U value) {
		// null도 유지할 수 있어야 하면 그냥 first = value로 프로그래밍함
		first = Objects.requireNonNull(value);
	}
	public void setSecond(V value) {
		second = Objects.requireNonNull(value);
	}
	// 아래와 같이 정의하면 모호하기 때문에 문법 오류
    // U, V가 똑같은 타입이 들어올 수 있기 때문에
	//public void set(U value) { first = value; }
	//public void set(V value) { second = value; }
} // class Pair<U, V>


받을 수 있지만 이렇게 list에 add하거나 할 수는 없다.



이렇게 new 타입변수 배열이 불가능하기 때문에 object 배열은 만들어 주었지만


이렇게 타입변환을 해줘야 한다. 하지만 이게 타입변환이 될지는 확신할 수 없기 때문에 Warning뜬다 그래서 @SuppressWarnings("unchecked")로 처리를 해줄 수 밖에 없다.

0개의 댓글