Java_제네릭스

song·2023년 10월 20일

Java

목록 보기
31/36

제네릭 (지네릭스, 제네릭스, generic)

  • 변수 선언 시, 메서드에 매개변수 사용할 때 모든 변수는 자료형을 가져야 한다.
  • 모두 하나의 자료형으로 구현하지만, 필요에 따라 바꿀 필요가 있다. (하지만 선언할 때 이미 정해놔서 못바꾼다. )
  • 어떤 값이 하나의 자료형이 아닌 여러 참조 자료형을 사용할 수 있도록 프로그래밍 한 것을 제네릭이라고 한다.

다이아몬드 연산자 <> 를 이용

  • T : Type
  • K : Key
  • V : Value
  • E : Element
  • N : Number
  • ? : 모든 타입 (와일드카드)
  • 이렇게 구분은 하지만 이건 컴퓨터가 구분하는 것이 아니라 사람이 구분하는 것이라 뭘 쓰던 상관은 없다.
  • 컴퓨터는 자료형 하나가 올건데 뭐가 올진 아직 모른다고 인식한다.

장점

  1. 타입의 안전성
    • 의도하지 않은 타입의 객체가 저장되지 못하게 막기
    • ArrayList<String> arrList = new ArrayList<>(); 라고 하면, 이제 arrList에는 String 자료형만 들어올 수 있다.
      다른형 들어오면 에러
  2. 불필요한 형변환을 줄임(코드의 간결함)
    • 1번 이유의 연장선인데, 타입을 미리 지정해놓음으로써 다른 타입의 객체가 저장되지 않기 때문에 나중에 객체를 꺼내서 사용할 때 형변환을 통한 타입을 맞출 필요가 없어서 코드를 간결하게 짤 수 있다.

사용 방법

class Coffee{
	void makeCoffee(){}
}
class Soda{
	void makeSoda(){}
}
class A<T>{
	private T ingrement;
    public T getIngrement(){return ingrement;}
    public void setIngrement(T ingrement){this.ingrement = ingrement;}
    
}
public class Main{
	public static void main(String[] args){
    	A<Coffee> a = new A<Coffee>();
        a.setIngrement(new Coffee);
        a.getIngrement().makeCoffee();
    }
}
  • A<Coffee> a = new A<Coffee>(); 여기서 뒤에 Coffee는 생략 가능하다.
    A<Coffee> a = new A<>();

자료형 제한 방법

  • 제네릭이라고해서 모든 클래스를 받게 냅두면 안된다.
    그래서 제네릭 클래스의 자료형<T>에 제한을 둘 수 있다.
  • <T> -> <T extends 부모클래스>로 해주면 된다.
    이 말은 부모클래스를 상속받은 객체들만 허용하겠다는 뜻
  • 이곳에는 implements 인터페이스는 할 수 없다.
    객체(class) 끼리만 얘기하는 것이다.
    abstract class Parent{
    	public abstract void makeDrink();
    }
    class Coffee extends Parent{
    	public void makeDrink(){System.out.println("Coffee");}
    }
    class Soda extends Parent{
    	public void makeDrink(){System.out.println("Soda");}
    }
    class Milk{
    	public void makeDrink(){System.out.println("Milk");}
    }
    class A<T extends Parent>{
    		private T ingrement;
      public T getIngrement(){return ingrement;}
      public void setIngrement(T ingrement){this.ingrement = ingrement;}  
    }
    public class Main{
        public static void main(String[] args){
            A<Coffee> a = new A<Coffee>();
            a.setIngrement(new Coffee);
            a.getIngrement().makeCoffee();
        }
    }
    이렇게 extends로 제한을 둔다.

두번째 사용 방법

class Coke{
    public void ttt(){
        System.out.println("Coke 출력");
    }
}
class 음료자판기<T extends Coke>{
    public void tmp(T t){
        t.ttt();
    }
}

public class Main {
    public static void main(String[] args) {
        음료자판기<Coke> c = new 음료자판기<>();
        c.tmp(new Coke());
    }
}

여기서 class 음료자판기<T>{} T만 받아서 t.ttt()하면 오류난다. 음료자판기 class 내에서는 누구의 ttt()를 실행해야 하는지 모르기 때문에 오류가 난다.
밑에 Main에서 음료자판기<Coke>로 인스턴스생성을 하는 것은 Main에서 Coke관련해서 음료자판기를 사용한다는 뜻이지 직관적으로 대입한다는 뜻은 아니다.

  • 오류해결
    1. 형변환
      ((Coke)t).ttt();
      위와같이 직접적으로 형변환을 해서 사용할 수 있다.
      하지만 이렇게 하면 제네릭스를 사용하는 의미가 없어진다.
    2. <T extends Coke 혹은 Coke부모>
      위와같이 어떤 것 관련해서 만들건지 알려준다.
      이렇게 하면 Coke와 관련된 객체구나 하고 인식해서 사용할 수 있다.
profile
계속 나아가기

0개의 댓글