제네릭

김주언·2022년 8월 10일

JAVA

목록 보기
6/10

Generic

클래스에서 사용하는 변수의 자료형이 여러개일 수 있고, 그 기능은 (메서드) 동일한 경우 클래스의 자료형을 특정하지 않고 이후에 해당 클래스를 사용할 때 지정할 수 있도록 한다.

실제 사용되는 자료형의 변환은 컴파일러에 의해 검증되므로 안정적인 프로그래밍 방식이다. 컬렉션 프레임워크에서 많이 사용된다.

제네릭 타입을 사용하지 않는 경우의 예

재료가 Powder인 경우

public class ThreeDPrinter1{
	private Powder material;
	
	public void setMaterial(Powder material) {
		this.material = material;
	}
	
	public Powder getMaterial() {
		return material;
	}
}

재료가 Plastic인 경우

public class ThreeDPrinter2{
	private Plastic material;
	
	public void setMaterial(Plastic material) {
		this.material = material;
	}
	
	public Plastic getMaterial() {
		return material;
	}
}
  • 여러 타입을 대체하기 위해 Object를 사용할 수 있음
public class ThreeDPrinter{

	private Object material;
	
	public void setMaterial(Object material) {
		this.material = material;
	}
	
	public Object getMaterial() {
		return material;
	}
}

대신 Object를 사용하는 경우는 형 변환을 해줘야한다.

ThreeDPrinter printer = new ThreeDPrinter();

Powder powder = new Powder();
printer.setMaterial(powder);

Powder p = (Powder)printer.getMaterial();

제네릭 클래스 사용하는 경우

GenericPrinter.java

public class GenericPrinter<T> {
	private T material;
	
	public void setMaterial(T material) {
		this.material = material;
	}
	
	public T getMaterial() {
		return material;
	}
	
	public String toString(){
		return material.toString();
	}
}
  • 자료형 매개변수 T(type parameter) : 이 클래스를 사용하는 시점에 실제 사용할 자료형을 지정, static 변수는 사용할 수 없음
  • GenericPrinter : 제네릭 자료형
  • E : element, K: key, V : value 등 여러 알파벳을 의미에 따라 사용 가능

제네릭 클래스 사용하기

Powder.java

public class Powder {
	
	public String toString() {
		return "재료는 Powder 입니다";
	}
}

Plastic.java

public class Plastic {

	public String toString() {
		return "재료는 Plastic 입니다";
	}
}

GenericPrinter.java

public class GenericPrinter<T> {
	private T material;   //T 자료형으로 선언한 변수
	
	public void setMaterial(T material) {
		this.material = material;
	}
	
	public T getMaterial() {   //T 자료형을 반환하는 제네릭 메서드
		return material;
	}
	
	public String toString(){
		return material.toString();
	}
}

GenericPrinterTest.java

public class GeneriPrinterTest {

	public static void main(String[] args) {

		GenericPrinter<Powder> powderPrinter = new GenericPrinter<Powder>();
		powderPrinter.setMaterial(new Powder());
		System.out.println(powderPrinter);
		
		GenericPrinter<Plastic> plasticPrinter = new GenericPrinter<Plastic>();
		plasticPrinter.setMaterial(new Plastic());
		System.out.println(plasticPrinter);
		
	}

}

다이아몬드 연산자 <>

  • ArrayList list = new ArrayList<>(); //다이아몬든 연산자 내부에서 자료형은 생략가능 함
  • 제네릭에서 자료형 추론(자바 10부터) ArrayList list = new ArrayList() => var list = new ArrayList();

<T extends 클래스> 사용하기

상위 클래스의 필요성

상위클래스를 상속받음으로써 T 자료형의 범위를 제한할 수 있다. 또한 상위 클래스에서 선언하거나 정의하는 메서드를 활용할 수 있게 된다.

상속을 받지 않는 경우에는 T는 Object로 변환되어 Object 클래스가 기본으로 제공하는 메서드만 사용할 수 있다.

T extends 사용 예제

  • GenericPrinter 에 material 변수의 자료형을 상속받아 구현
  • T에 무작위 클래스가 들어갈 수 없게 Material 클래스를 상속받은 클래스로 한정

Untitled

Material.java

public abstract class Material {
	public abstract void doPrinting();
}

Powder.java

public class Powder extends Material {
	public void doPrinting() {
		System.out.println("Powder 재료로 출력합니다");
	}
	public String toString() {
		return "재료는 Powder";
	}
}

Plastic.java

public class Plastic extends Material{

	public void doPrinting() {
		System.out.println("Plastic 재료로 출력합니다");
	}
	
	public String toString() {
		return "재료는 Plastic 입니다";
	}
}

GenericPrinter.java

public class GenericPrinter<T extends Material> {
	private T material;
	
	public void setMaterial(T material) {
		this.material = material;
	}
	
	public T getMaterial() {
		return material;
	}
	
	public String toString(){
		return material.toString();
	}
	
	public void printing() {
		material.doPrinting();
	}
}

GenericPrinterTest.java

public class GenericPrinterTest {

	public static void main(String[] args) {

		GenericPrinter<Powder> powderPrinter = new GenericPrinter<Powder>();
		powderPrinter.setMaterial(new Powder());
		Powder powder = powderPrinter.getMaterial(); // 형변환 하지 않음
		System.out.println(powderPrinter);
		
		GenericPrinter<Plastic> plasticPrinter = new GenericPrinter<Plastic>();
		plasticPrinter.setMaterial(new Plastic());
		Plastic plastic = plasticPrinter.getMaterial(); // 형변환 하지 않음
		System.out.println(plasticPrinter);
		
	/*	GenericPrinter powderPrinter2 = new GenericPrinter();
		powderPrinter2.setMaterial(new Powder());
		Powder powder = (Powder)powderPrinter.getMaterial();
		System.out.println(powderPrinter);
		*/
		//GenericPrinter<Water> printer = new GenericPrinter<Water>();	
	}
}

제네릭 메서드

자료형 매개변수를 메서드의 매개변수나 반환값으로 가지는 메서드는 자료형 매개 변수가 하나 이상인 경우가 있다. 제네릭 클래스가 아니더라도 내부에 제네릭 메서드를 구현하여 사용이 가능하다.

public <자료형 매개변수> 반환형 메서드이름 (자료형 매개변수 ... ) { ... } 

제네릭 메서드의 활용 예제

  • 두 점(top, bottom)을 기준으로 사각형을 만들 때 사각형의 너비를 구하는 메서드 구현하기
  • 두 점은 정수인 경우도 있고, 실수인 경우도 있으므로 제네릭 타입을 사용하여 구현한다.

Point.java

public class Point<T, V> {
	
	T x;
	V y;
	
	Point(T x, V y){
		this.x = x;
		this.y = y;
	}
	
	public  T getX() {
			return x;
	}

	public V getY() {
		return y;
    }
}

GenericMethod.java

public class GenericMethod {

	public static <T, V> double makeRectangle(Point<T, V> p1, Point<T, V> p2) {
		double left = ((Number)p1.getX()).doubleValue();
		double right =((Number)p2.getX()).doubleValue();
		double top = ((Number)p1.getY()).doubleValue();
		double bottom = ((Number)p2.getY()).doubleValue();
		
		double width = right - left;
		double height = bottom - top;
		
		return width * height;
	}
	
	public static void main(String[] args) {
		
		Point<Integer, Double> p1 = new Point<Integer, Double>(0, 0.0);
		Point<Integer, Double> p2 = new Point<>(10, 10.0);
		
		double rect = GenericMethod.<Integer, Double>makeRectangle(p1, p2);
		System.out.println("두 점으로 만들어진 사각형의 넓이는 " + rect + "입니다.");
	}
}

순차적으로 자료를 관리하는 List 인터페이스를 구현한 클래스와 그 활용

멤버십 관리하기

  • Member 클래스를 만들고, 아이디와 이름을 멤버 변수로 선언
  • Member 클래스로 생성된 인스턴스들을 관리하는 클래스를 컬렉션 프레임워크 클래스들을 활용하여 구현한다.

ArrayList 활용하기

  • 멤버를 순차적으로 관리함

Member.java

public class Member {
	
	private int memberId;        //회원 아이디
	private String memberName;   //회원 이름

	public Member(int memberId, String memberName){ //생성자
		this.memberId = memberId;
		this.memberName = memberName;
	}
	
	public int getMemberId() {  //
		return memberId;
	}
	public void setMemberId(int memberId) {
		this.memberId = memberId;
	}
	public String getMemberName() {
		return memberName;
	}
	public void setMemberName(String memberName) {
		this.memberName = memberName;
	}
	
	@Override
	public String toString(){   //toString 메소드 오버로딩
		return memberName + " 회원님의 아이디는 " + memberId + "입니다";
	}
}

MemberArrayList.java

public class MemberArrayList {

	private ArrayList<Member> arrayList;  // ArrayList 선언 

	public MemberArrayList(){
		arrayList = new ArrayList<Member>();  //멤버로 선언한 ArrayList 생성
	}
	
	public void addMember(Member member){  //ArrayList 에 멤버 추가
		arrayList.add(member);
	}
	
	public boolean removeMember(int memberId){  // 멤버 아이디를 매개변수로, 삭제 여부를 반환
		
		for(int i =0; i<arrayList.size(); i++){ // 해당 아이디를 가진 멤버를 ArrayList에서 찾음
			Member member = arrayList.get(i);
			int tempId = member.getMemberId();
			if(tempId == memberId){            // 멤버아이디가 매개변수와 일치하면 
				arrayList.remove(i);           // 해당 멤버를 삭제
				return true;                   // true 반환
			}
		}
		
		System.out.println(memberId + "가 존재하지 않습니다");  //for 가 끝날때 까지 return 이 안된경우
		return false;                   
	}
	
	public void showAllMember(){
		for(Member member : arrayList){
			System.out.println(member);
		}
		System.out.println();
	}
}

MemberArrayListTest.java


public class MemberArrayListTest {

	public static void main(String[] args) {

		MemberArrayList memberArrayList = new MemberArrayList();
		
		Member memberLee = new Member(1001, "이순신");
		Member memberKim = new Member(1002, "김유신");
		Member memberKang = new Member(1003, "강감찬");
		Member memberHong = new Member(1004, "홍길동");
		
		memberArrayList.addMember(memberLee);
		memberArrayList.addMember(memberKim);
		memberArrayList.addMember(memberKang);
		memberArrayList.addMember(memberHong);
		
		memberArrayList.showAllMember();
		
		memberArrayList.removeMember(memberHong.getMemberId());
		memberArrayList.showAllMember();
	}
}

Collection 요소를 순회하는 Iterator

요소의 순회란?

  • 컬렉션 프레임워크에 저장된 요소들을 하나씩 차례로 참조하는것
  • 순서가 있는 List인터페이스의 경우는 Iterator를 사용 하지 않고 get(i) 메서드를 활용할 수 있음
  • Set 인터페이스의 경우 get(i) 메서드가 제공되지 않으므로 Iterator를 활용하여 객체를 순회함

Iterator 사용하기

  • boolean hasNext() : 이후에 요소가 더 있는지를 체크하는 메서드, 요소가 있다면 true를 반환
  • E next() : 다음에 있는 요소를 반환

MemberArrayList.java 의 removeMember() 메서드를 Iterator를 활용하여 구현

public boolean removeMember(int memberId){  // 멤버 아이디를 매개변수로, 삭제 여부를 반환
	
		Iterator<Member> ir = arrayList.iterator();
		while(ir.hasNext()) {
			Member member = ir.next();
			int tempId = member.getMemberId();
			if(tempId == memberId){            // 멤버아이디가 매개변수와 일치하면 
				arrayList.remove(member);           // 해당 멤버를 삭제
				return true;                   // true 반환
			}
		}
		
		System.out.println(memberId + "가 존재하지 않습니다");  //for 가 끝날때 까지 return 이 안된경우
		return false;                   
}
profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글