[F-lab 모각코 챌린지 30일차] TIL

JeongheeKim·2023년 6월 30일

TIL

목록 보기
30/66

학습계획


  • 제네릭

Today I Learned


  • 제네릭이란 결정되지 않은 타입을 파라미터로 처리하고 실제 사용할 때 파라미터의 구체적인 타입을 대체시키는 기능
  • 제네릭은 타입 형 변환에서 발생할 수 있는 문제점을 컴파일 시 점검할수 있게 JDK 1.5에서 등장하였다.
  • 타입을 지정하지 않으면 Object 타입이 암묵적으로 사용된다.
    public class 클래스명<A> {}
    public interface 인터페이스명<A>{}

❓자바에서 제네릭을 사용하는 이유와 제네릭 타입의 예시를 들어주세요.

  • 제네릭을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일과정에서 제거할 수 있다.
  • 실행 시 타입에러가 나는것보다 컴파일 시 타입체크를 강하게 검사하여 에러를 사전 방지하는것이 좋다.
  • 제네릭을 사용하면 타입을 제한하기때문에 요소를 찾아올 때 타입변환을 할 필요가 없어 프로그램 성능이 향상되는 효과를 얻을 수 있다.

제네릭 타입

  • E : 요소(Collection에 주로 사용됨)
  • K : 키
  • N : 숫자
  • T : 타입
  • V : 값
  • S, U, V : 두번째, 세번째, 네번째에 선언된 타입

제네릭 메서드

메서드의 선언부에 제네릭 타입이 선언된 메서드이며, 제네릭의 위치는 반환 타입 앞이다.

예)

public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }

❓제네릭 클래스와 제네릭 인터페이스에 대해 설명해주세요. 또한, 제네릭 타입의 원시 타입(raw type)이란 무엇이고 어떤 문제점이 있을까요?

제네릭 클래스

제네릭 클래스는 매개변수나 함수를 매개변수로 일반화 하여 T타입에 모든유형을 추가할 수 있다.

예제1)

public class Home {
	public void turnOnLight() {
		System.out.println("전등을 켭니다.");
	}
}

public class Car {
	public void run() {
		System.out.println("자동차가 달립니다.");
	}
}

public class HomeAgency implements Rentable<Home> {
	@Override
	public Home rent() {
		return new Home();
	}
}

public class CarAgency implements Rentable<Car> {
	@Override
	public Car rent() {
		return new Car();
	}
}

public class GenericExample {
	public static void main(String[] args) {
		HomeAgency homeAgency = new HomeAgency();
		Home home = homeAgency.rent();
		home.turnOnLight();

		CarAgency carAgency = new CarAgency();
		Car car = carAgency.rent();
		car.run();
	}
}

예제2)

public class GenericsTypeOld {
    private Object t;

    public Object get() {
        return t;
    }

    public void set(Object t) {
        this.t = t;
    }

    public static void main(String[] args) {
        GenericsTypeOld type = new GenericsTypeOld();
        type.set("test str");
        String str = (String) type.get();
    }
}

위의 코드는 매번 type casting을 해줘야하며, 만약 맞지않는 casting을 할 경우 ClassCastException이 발생할 수 있다.

public class GenericsTypeOld<T> {
    private T t;

    public T get() {
        return this.t;
    }

    public void set(T t1) {
        this.t = t1;
    }

    public static void main(String[] args) {
        GenericsTypeOld<String> type = new GenericsTypeOld<>();
        type.set("test str");
        
        GenericsTypeOld type1 = new GenericsTypeOld();//Raw use of parameterized class 'GenericsTypeOld'
        type1.set("Test 2");
        type1.set(10);
    }
}

클래스 매개변수에 제네릭 타입을 지정하므로써 클래스 생성 시 타입을 지정하여 생성할 수 있다. 그러므로 타입 캐스팅의 불편함이 제거되었다.

반면에 타입을 지정하지 않은 경우 GenericsTypeOld type1 = new GenericsTypeOld();는 RawType(타입 변수 또는 타입 매개변수가 없는 클래스나 인터페이스를 뜻함.)으로 선언되어 Object타입으로 지정된다. set메서드로 String, int가 가능해졌다. Runtime오류를 피하기 위해서 사용할 타입의 지정이 필요하다.

제네릭 인터페이스

  • 제네릭 인터페이스는 대표적인 예로 Comparable 인터페이스가 있다. Comparable를 구현한 클래스는 T타입의 요소들을 비교하여 정렬할 수 있다.
  • 제네릭 인터페이스는 다양한 타입으로 구현될 수 있다.
package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}
  • 자바 제네릭 인터페이스 구현 예시
public interface Fruit <T> {
    public void taste(T fruit);
}

public class AnyFruit<T> implements Fruit<T>{
    @Override
    public void taste(T fruit) {
        String fruitName = fruit.getClass().getName();
        System.out.println("fruitName = " + fruitName);
    }
}

public class Banana {
}

public class TestFruit {
    public static void main(String[] args) {
        Banana banana = new Banana();
        AnyFruit<Banana> fruit1 = new AnyFruit<>();
        fruit1.taste(banana);//fruitName = Banana

    }
}

제네릭 클래스와 제네릭 인터페이스의 개념과 예시, 그리고 Raw Type의 문제점 등을 잘 설명했습니다. 하지만, Raw Type에 작성된 코드를 사용하는 경우와 실행할 때 발생할 수 있는 문제점을 더 자세히 설명하는 것이 좋을 것 같습니다.

제네릭 클래스에서 타입 파라미터의 상한과 하한에 대해 설명해주세요. 상한과 하한을 사용하면 어떤 이점이 있을까요?

제네릭에 사용하는 타입 파라미터를 제한하는 방법중 상한과 하한이 있다. 이는 자식 또는 구현관계에 있는 타입만 대체할 수 있다. 이를 통해 제네릭을 다형성있게 사용 할 수 있다.

타입을 제한하므로서, 타입을 받는 쪽에서 타입캐스팅에 대한 범위를 알 수 있다.

https://www.codetab.org/tutorial/java-generics/bounded-wildcards/

https://www.codetab.org/tutorial/java-generics/bounded-wildcards/

제한하지 않는 경우(Unbounded Wildcards)

public void display(List<? extends Pet> list) {
}

display(new ArrayList<Pet>());         
display(new ArrayList<Dog>());      
display(new ArrayList<Bulldog>());
display(new ArrayList<Cat>());      
display(new ArrayList<Persian>());      

display(new ArrayList<String>());   
display(new ArrayList<Integer>());

❓ 와일드카드를 제네릭 타입 파라미터로 사용할 때, ? extends T? super T의 차이점은 무엇인가요? 이 차이점을 이용하여 어떤 상황에서는 extends를 사용해야 하는지, 어떤 상황에서는 super를 사용해야 하는지 설명해주세요.

상한(Upper Bounded Wildcards) - ? extends T

T 클래스를 상속받은 클래스나 T클래스 자신 클래스를 타입 파라미터로 허용.

타입 파라미터의 범위를 상속 클래스 자체 및 상속클래스의 subtype으로 범위를 제한한다.

public void display(List<? extends Pet> list) {
}

display(new ArrayList<Pet>());         
display(new ArrayList<Dog>());      
display(new ArrayList<Bulldog>());
display(new ArrayList<Cat>());      
display(new ArrayList<Persian>());      

display(new ArrayList<String>());   //error
display(new ArrayList<Integer>());  //error

? extends T의 경우 컬렉션 요소를 읽을 경우 컴파일러가 타입 안정성을 보장할 수 있기에 Collection요소를 읽을때 사용하는것이 좋다.

public static void main(String[] args) {
		List<Integer> integerList = Arrays.asList(1,2,3,4);
		List<Double> doubleList = Arrays.asList(1.1, 2.1, 3.1, 4.1);

		printNumberList(integerList);
		printNumberList(doubleList);
	}

	public static void printNumberList(List<? extends Number> numbers) {
		for (Number number :  numbers) {
			System.out.println("number : " +  number);
		}
	}

하한(Lower Bounded Wildcards) - ? super T

T 클래스를 상위 클래스나 T클래스 자신 클래스를 타입 파라미터로 허용.

타입 파라미터의 범위를 상속받는 클래스 supertype 및 super type클래스 범위를 제한한다.

(super 키워드 : 상속받은 자식 클래스와 부모클래스에 동일한 변수명이 있을 경우 부모클래스의 변수를 선택할 경우 사용)

public void display(List<? super Buldog> list) {
}

display(new ArrayList<Pet>());         
display(new ArrayList<Dog>());      
display(new ArrayList<Bulldog>());
display(new ArrayList<Cat>());      //error
display(new ArrayList<Persian>());  //error    

display(new ArrayList<String>());   //error
display(new ArrayList<Integer>());  //error

컬렉션에 요소를 추가할 경우 ? super T 타입을 제한하여 컴파일러가 타입 안정성을 보장 할 수 있다.

	public static void main(String[] args) {
		List<Number> numberList = new ArrayList<>();
		List<Object> objectList = new ArrayList<>();

		printNumberList(numberList);
		printNumberList(objectList);
	}

	public static void printNumberList(List<? super Integer> numbers) {
		numbers.add(44);
		numbers.add(new Integer(10));
	}

제네릭이 자바에 추가된 이유는 무엇인가요?

  • 결정되지 않은 타입을 파라미터로 처리하고 실제 사용할 때 파라미터의 구체적인 타입을 대체시키기 위해

제네릭 타입의 이름은 T나 E 처럼 하나의 캐릭터로만 선언되어야 하나요?

  • X

메소드에서 제네릭 타입을 명시적으로 지정하기 애매할 경우에는 < > 안에 어떤 기호를 넣어 주어야 하나요?

  • 을 선언하여 와일드 타입임을 명시할 수 있다.

메소드에서 제네릭 타입을 명시적으로 지정하기에는 애매하지만, 어떤 클래스의 상속을 받은 특정 타입만 가능하다는 것은 나타내려면 < > 안에 어떤 기호를 넣어 주어야 하나요?

  • extends

제네릭 선언시 wildcard라는 것을 선언했을 때 어떤 제약사항이 있나요?

  • object타입으로만 사용해야한다.

메소드를 제네릭하게 선언하려면 리턴타입 앞에 어떤 것을 추가해 주면 되나요?

  • 꺽쇠 안에 원한느 제네릭 타입을 명시하여 제네릭한 메소드 선언이 가능

0개의 댓글