- 자바에서 제네릭(Generics)은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다. 객체별로 다른 타입의 자료가 저장될 수 있도록 한다.
ArrayList<String> list = new ArrayList<>();
- 이처럼 제네릭은 배열의 타입을 지정하듯이 리스트 자료형 같은 컬렉션 클래스나 메소드에서 사용할 내부 데이터
타입(type)
을파라미터(parameter)
주듯이 외부에서 지정하는 이른바 타입을 변수화 한 기능이라고 이해하면 된다.
- 제네릭은
< >
꺾쇠 괄호 키워드를 사용하는데 이를 다이아몬드 연산자라고 한다. 그리고 이 꺾쇠 괄호 안에 식별자 기호를 지정함으로써 파라미터화 할 수 있다. 이것을 마치 메소드가 매개변수를 받아 사용하는 것과 비슷하여 제네릭의 타입 매개변수(parameter) / 타입 변수라고 부른다.List<t> //--- 타입 매개변수 List<String> stringList = new ArrayList<String> ( );
- 이 타입 매개변수는 제네릭을 이용한 클래스나 메소드를 설계할 때 사용된다.
- 타입 파라미터 생략
- 제네릭 객체를 사용하는 문법 형태를 보면 양쪽 두 군데에 꺾쇠 괄호 제네릭 타입을 지정함을 볼 수 있다. 하지만 맨 앞에서 클래스명과 함께 타입을 지정해 주었는데 굳이 생성자까지 제네릭을 지정해 줄 필요가 없다. (중복)
FruitBox<Apple> intBox = new FruitBox<Apple>(); // 다음과 같이 new 생성자 부분의 제네릭의 타입 매개변수는 생략할 수 있다. FruitBox<Apple> intBox = new FruitBox<>();
- 제네릭에서 할당 받을 수 있는 타입은
Reference
타입 뿐이다. 즉, int형 이나 double형 같은 자바 원시 타입(Primitive Type)을 제네릭 타입 파라미터로 넘길 수 없다는 말이다.// 기본 타입 int는 사용 불가 !!! List<int> intList = new List<>(); // Wrapper 클래스로 넘겨주어야 한다. (내부에서 자동으로 언박싱되어 원시 타입으로 이용) List<Integer> integerList = new List<>();
사용 예시//가상의 과일 박스 public class FruitBox<T> { //필드 private T item; //과일을 포장하는 메소드 public void pack(T item) { this.item = item; } //과일을 리턴하는 메소드 public T unPack() { return item; } } --------------------------------------------------------------------------- package test.mypac; public class Apple { } ---------------------------------------------------------------------------- public class Melon { } ---------------------------------------------------------------------------- public class Orange { } --------------------------------------------------------------------------- package test.main; import test.mypac.*; public class MainCLass { @SuppressWarnings("unused") public static void main(String[] args) { //FruitBox 객체를 생성해서 참조값을 box라는 이름의 지역 변수에 담아보세요 FruitBox<Apple> box =new FruitBox<Apple>(); //box에 담긴 참조값을 이용해서 pack() 메소드를 호출해보세요. box.pack(new Apple()); //box에 담긴 참조값을 이용해서 unpack() 메소드를 호출하고 리턴되는 값을 //fruit라는 지역변수에 담아보세요. Apple fruit = box.unPack(); //여기서 생성한 박스에 Melon을 담을 수 있는지 확인해 보세요 FruitBox<Melon> box2 =new FruitBox<Melon>(); box2.pack(new Melon()); Melon fruit2 =box2.unPack(); //Orange도 테스트 해보세요 //객체 생성시에 Generic 클래스는 생략 가능하다 FruitBox<Orange> box3 =new FruitBox<>(); box3.pack(new Orange()); Orange fruit3 =box3.unPack(); } }
- 인스턴스 메서드와 인스터스 변수를 일절 제공하지 않고, 정적 메서드와 변수만을 제공하는 클래스를 뜻한다.
- 클래스 본래의 목적인 '데이터와 데이터 처리를 위한 로직의 캡슐화'를 실행하는 것이 아닌,
'비슷한 기능의 메서드와 상수를 모아서 캡슐화'한 것이 유틸리티 클래스이다.사용 예시
package test.mypac; public class Car { //필드 private String name; //생성자 public Car(String name) { this.name=name; } //메소드 public void drive() { System.out.println(this.name+" 이(가) 달려요!"); } } --------------------------------------------------------------------------- package test.main; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import test.mypac.Car; public class MainClass04 { public static void main(String[] args) { //Car type을 저장할 수 있는 ArrayList 객체를 생성해서 //참조값을 List 인터페이스 type 지역변수 cars에 담아보세요. List<Car> cars = new ArrayList<>(); //Car 객체(3개)를 생성해서 List 객체에 저장해보세요. cars.add(new Car("소나타")); cars.add(new Car("그랜저")); cars.add(new Car("아반떼")); //일반 for 문을 이용해서 List 객체에 저장된 모든 Car 객체의 //drive() 메소드를 순서대로 호출해 보세요. for (int i = 0; i < cars.size(); i++) { cars.get(i).drive(); } System.out.println("------------------"); //확장 for 문을 이용해서 List 객체에 저장된 모든 Car 객체의 //drive() 메소드를 순서대로 호출해 보세요. for (Car tmp : cars) { tmp.drive(); } System.out.println("------------------"); //Consumer 인터페이스를 활용해서 List 객체에 저장된 모든 Car 객체의 //drive() 메소드를 순서대로 호출해 보세요. Consumer<Car> car =new Consumer<Car>() { @Override public void accept(Car t) { t.drive(); } }; cars.forEach(car); System.out.println("------------------"); cars.forEach((t)->{ t.drive(); }); } }