본문 바로가기

일/JAVA

java13.generic

슬슬 어려워져,,,그래도 화이팅 ㅎ

/*

제네릭(Generic)

자바 5부터 제네릭타입이 새롭게 추가되었다. 제네릭타입을 사용하게 되면 잘못된 타입이 사용되는 가능성을

컴파일단계부터 제거할 수 있게 되었다. 제네릭은 컬렉션, 람다식, 스트림, NIO(New Input Out)에 널리

사용되기 때문에 제네릭을 이해하지 못한다면 자바의 API문서등을 이해할 수 없다.

 

제네릭은 클래스와 인터페이스, 메서드를 정의할 때 타입을 매개값으로 사용할 수 있도록 한다. 타입파라미터는

코드 작성시에 구체적인 타입으로 선언되어서 다양한 객체들을 생성할 수 있도록 한다.

 

제네릭을 사용하는 이점은

1. 컴파일시에 강한 타입체크를 할 수 있다.

자바 컴파일러는 코드에서 잘못 사용되는 타입때문에 발생하는 문제들을 사전에 제거하기 위해 제네릭코드

에서 강한 타입체크를 한다. 실행시에 타입에러가 발생하는 것보다 컴파일시 미리 타입을 강하게 제한

해서 에러를 미연에 방지할 수 있다.

2. 타입변환 제거한다.

비제네릭코드는 불필요한 타입변환을 하기 때문에 프로그램성능에 악영향을 미치게 된다. 제네릭을 사용하면

사전에 정해진 타입으로만 제한 시키기 때문에 불필요한 타입 변환을 할 필요가 없어 프로그램의 성능이 향상

된다.

 

제네릭문법

 

제네릭타입(클래스<T>, 인터페이스<T>)

제네릭타입은 타입을 파라미터로 갖는 클래스와 인터페이스를 말한다. 제네릭 타입은 클래스 또는 인터페이스이름

뒤에 다이아몬드부호 "<>"를 붙이고 사이에 타입파라미터가 위치한다. 타입파라미터는 변수명과 동일한 규칙에 따라

선언이 가능하지만 일반적으로 대문자 알파벳 한글자로 선언한다.

*/

nongeneric type

public class BoxMain {

 

public static void main(String[] args) {

Box 과일상자 = new Box();

// Apple 사과 = new Apple();

과일상자.setObject(new Apple()); // 자동형변환 Apple -> Object

Apple 사과 = (Apple) 과일상자.getObject(); // 강제형변환 Object -> Apple

System.out.println(사과);

System.out.println(사과.getClass());

과일상자.setObject(new Hammer());

// Apple 사과2 = (Apple) 과일상자.getObject();

Hammer 망치= (Hammer) 과일상자.getObject();

// System.out.println(망치);

System.out.println("과일상자 = " + 망치.getClass());

System.out.println();

Box 도구상자 = new Box();

도구상자.setObject(new Hammer()); // 자동형변환 Hammer -> Object

망치 = (Hammer) 도구상자.getObject(); // 강제형변환Object -> Hammer

System.out.println(망치.getClass());

 

도구상자.setObject(new Apple());

사과 = (Apple) 도구상자.getObject();

System.out.println("도구상자 = " + 사과.getClass());

 

도구상자.setObject(new String("아담"));

String 아담 = (String) 도구상자.getObject();

System.out.println("도구상자 = " + 아담.getClass());

 

과일상자.setObject(new String("홍길동"));

과일상자.setObject(Integer.valueOf(1));

과일상자.setObject(Double.valueOf(1.0));

과일상자.setObject('A');

과일상자.setObject(new int[] {1,2,3,4,5});

}

}

 

class Box{

private Object object;

public Object getObject() {return this.object; };

public void setObject(Object object) {this.object=object;}

}

 

class Apple{}

class Hammer{}

generic type

public class BoxMain {

 

public static void main(String[] args) {

Box<Apple> 과일상자 = new Box();

과일상자.setObject(new Apple());

// 과일상자.setObject(new Hammer());

// 제네릭은 컴파일 단계에서부터 이러발생을 방지할 수 있다.

// 객체저장시에 사전에 특정 객체만 주입이 되도록 제한하는 방법이다.

// 과일상자.setObject(new Hammer()); 에러

 

 

// 주의사항

// 좌변에만 제네릭을 정의하면 사전에 에러를 방지할 수 있지만

// 우변에만 제네릭을 정의하면 non generic과 동일한ㄴ다.

// 즉, 우변에만 제네릭을 정의할 경우에는 객체제한을 할 수가 없다.

Box<Apple> 과일상자1 = new Box();

Box<Apple> 과일상자2 = new Box<>(); // 통상적인 선언방법

Box<Apple> 과일상자3 = new Box<Apple>();

Box 과일상자4 = new Box<Apple>(); // 제네릭으로 제한 불가

 

Apple 사과 = 과일상자.getObject();

System.out.println(사과);

 

Box<Hammer> 도구상자 = new Box();

도구상자.setObject(new Hammer());

Hammer 망치 = 도구상자.getObject();

System.out.println(망치);

// 도구상자.setObject(new apple());

 

Box<String> 문자상자 = new Box<>();

문자상자.setObject(new String("아담"));

String 아담 = 문자상자.getObject();

System.out.println(아담);

}

}

 

class Box<T>{

private T t;

public T getObject() {return t;}

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

}

 

class Apple{

@Override

public String toString() {

return "나는 사과입니다";

}

}

class Hammer{

@Override

public String toString() {

return "나는 망치입니다";

}

}

multi type

public class ProductMain {

 

public static void main(String[] args) {

// 1. non generic

Product product = new Product();

 

product.setKind(1);

int num = (int) product.getKind();

 

product.setKind("문자열");

String str = (String)product.getKind();

 

product.setKind(3.14f);

float flt = (float)product.getKind();

 

product.setKind(new Robot());

Robot robot = (Robot)product.getKind();

 

product.setKind(new Car());

Car car = (Car)product.getKind();

 

product.setKind(new TV());

TV tv = (TV)product.getKind();

 

 

 

// 2. generic

Product<Robot, String> 로봇 = new Product<>();

로봇.setKind(new Robot());

로봇.setModel("Super");

System.out.println(로봇.getKind()+로봇.getModel()+"로봇 입니다");

로봇.setModel("Standard");

System.out.println(로봇.getKind()+로봇.getModel()+"로봇 입니다");

로봇.setModel("Cheap");

System.out.println(로봇.getKind()+로봇.getModel()+"로봇 입니다");

 

// 로봇.setKind(1);

// 로봇.setKind("문자열");

// 로봇.setKind(new Car());

// 로봇.setKind(new TV());

 

Product<Car, String> 자동차 = new Product<Car, String>();

자동차.setKind(new Car());

자동차.setModel("포르쉐 911 카레라");

System.out.println(자동차.getModel());

자동차.setModel("벤츠 s500");

System.out.println(자동차.getModel());

System.out.println();

 

Product<TV, String> 텔레비전 = new Product<>();

텔레비전.setKind(new TV());

텔레비전.setModel("LED TV");

System.out.println(텔레비전.getModel());

텔레비전.setModel("LG OLED TV");

System.out.println(텔레비전.getModel());

System.out.println();

 

// 제네릭타입은 참조타입만 가능

Product<TV, Car> prod1 = new Product<>();

Product<Car, TV> prod2 = new Product<>();

Product<Car, Car> prod3 = new Product<>();

Product<Robot, TV> prod4 = new Product<>();

Product<String, Car> prod5 = new Product<>();

Product<String, String> prod6 = new Product<>();

Product<Integer, String> prod7 = new Product<>();

Product<int[], Student> prod8 = new Product<>();

 

// 참조타입이 아닌 경우

// Product<int, String> prod8 = new Product<>();

}

}

class Product<K, M>{

private K kind;

private M model;

 

public K getKind() {

return kind;

}

public void setKind(K kind) {

this.kind = kind;

}

public M getModel() {

return model;

}

public void setModel(M model) {

this.model = model;

}

 

}

class Robot{

@Override

public String toString() {

return "나는 ";

}

}

class Car{}

class TV{}

class Student{}

/*

제네릭메서드 : <T>리턴타입<T> 메서드명(T,t){}

제네릭메서드는 매개타입과 리턴타입으로 타입파라미터를 갖는 메서드를 말한다. 제네릭 메서드를

선언하는 방법은 리턴타입 앞에 <>를 추가하고 타입파라미터를 기술한 후에 리턴타입과 매개타입

으로 타입파라미터를 사용하면 된다.

 

public <T> 리턴타입<T> 메서드명(T,t){return 리턴타입}

제네릭메서드는 2가지 방식으로 호출할 수 있다. 코딩할 대 타입파라미터의 구체적인 타입을 명시

적으로 지정해도 되고, 컴파일러가 매개값의 타입으로 보고 구체적인 타입을 주저하도록 할 수

있다.

*/

public class MethodMain1 {

 

public static void main(String[] args) {

 

// 1. 제네릭메서드

Box<Integer> box1 = Util.boxing(1000);

System.out.println(box1.get());

System.out.println();

Box<String> box2 = Util.boxing(new String("망치"));

System.out.println(box2.get());

Box<Apple> box3 = Util.boxing(new Apple());

System.out.println(box3.get());

Box<Car> box4 = Util.boxing(new Car());

System.out.println(box4.get());

 

// 2. 일반메서드

// Box g_box = Util.method(1000);

Box g_box = null;

// g_box = Util.method(new String("망치"));

// g_box = Util.method(new Car());

// g_box = Util.method(new Apple());

// int g_val1 = (int)g_box.get();

g_box = Util.method(new Robot());

System.out.println(g_box.get());

// 3.

}

}

class Box<T>{

private T t;

public T get() {return t;}

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

}

class Util{

// 1. 일반메서드

public static Box method(Robot robot) {

Box box = new Box();

box.set(new Robot());

// box.set(new String("망치"));

return box;

}

// 2. 제네릭메서드

public static<T> Box<T> boxing(T t){

Box<T> box = new Box();

box.set(t);

return box;

}

}

class Robot{@Override

public String toString() {

return "나는 로봇";

}

}

class Apple{@Override

public String toString() {

return "나는 사과";

}

}

class Car{@Override

public String toString() {

return "나는 차";

}

}

/*

제한된 파라미터(<T extends 상위타입>)

타입파라미터에 지정되는 구체적인 데이터타입을 제한할 필요가 있을 경우, 예를 들어

숫자를 연산하는 제네릭메서드가 있다면 이 메서드는 매개값을 숫자타입만 전달되어야 한다.

즉, 매개값을 Number or 하위클래스(Byte, Integer, Double ...)등의 참조타입(객체)만 전달

되어야 한다. 이것이 제한될 타입파라미터(Bounded Type Parameter)가 필요한 이유이다..

 

제한된 파라미터를 선언하려면 타입파라미터 뒤에 extends키워드를 선언하고 상위타입을 명시

하면 된다. 상위타입은 클래스뿐만 아니라 인터페이스도 가능한데 인터페이스라고 해서 implements

로 선언하지 않고 클래스와 동일학ㅔ extends 키워드를 사용한다.

 

타입파라미터에 지정되는 구체적인 타입은 상위타입이거나 상위타입을 상속 또는 구현한

하위클래스, 하위인터페이스만 전달이 가능하다.

*/

public class BoundedMain {

 

public static void main(String[] args) {

int result = Util.compare(10, 10);

System.out.println(result);

result = Util.compare(10.5,10.0);

System.out.println(result);

 

// result = Util.compare(10,"10"); 에러

// Number와 하위타입만 대입하도록 제한(bounded)되어 있기 때문에 에러

result = Util.compare(20,10);

// 20, 10은 int타입이지맞 Number(extends한) 의 하위타입이기 때문에 대입이 가능.

// 내부적으로는 intIntefer로 Boxing이 된 후 대입

}

}

class Util{

public static <T extends Number> int compare(T t1, T t2){

Double x = t1.doubleValue();

Double y = t2.doubleValue();

return Double.compare(x, y);

}

}

/*

와일드카드(<?>,<? extends 상위타입>, <? super 하위타입>)

프로그램에서 ?을 일반적으로 와일드카드라고 한다. 제네릭타입을 매개갑ㅅ이나 리턴타입으로

사용할 때 구체적인 타입대신에 와일드카드를 사용할 수 있다. 와일드카드를 사용하는 형태는

다음과 같이 3가지 형태로 사용할 수 있다.

 

1.<?> : 타입의 제한이 없다.

2.<? extends 상위타입>: 상위타입포함 하위타입만 가능하다. 즉 상위타입이상은 제한

3.<? super 하위타입> : 하위타입포함 상위타입만 가능하다. 즉 하위타입이하는 제한

*/

public class Course<T> {

private String name;

private T[] students;

 

public Course(String name, int capacity) {

this.name = name;

this.students = (T[]) new Object[capacity]; // 수강인원

}

 

public String getName() {

return name;

}

 

public T[] getStudents() {

return students;

}

 

public void add(T t) {

for(int i=0;i<students.length;i++) {

if(students[i]==null) {

students[i]=t;

break;

}

}

}

}

public class Person {

private String name;

 

 

public String getName() {

return name;

}

 

public void setName(String name) {

this.name = name;

}

 

public Person(String name) {

this.name = name;

}

@Override

public String toString() {

return "Person [name=" + name + "]";

}

}

 

class Student extends Person{

public Student(String name) {

super(name);

}

}

 

class HighStudent extends Student{

public HighStudent(String name) {

super(name);

}

}

 

class Worker extends Person{

public Worker(String name) {

super(name);

}

}

public class WildCardMain {

 

public static void main(String[] args) {

 

// 1. 일반인 반 : 모든 수강등록이 가능한 코스

Course<Person> 일반인반 = new Course<>("일반인과정",5);

일반인반.add(new Person("일반인A"));

일반인반.add(new Worker("직장인A"));

일반인반.add(new Student("학생A"));

일반인반.add(new HighStudent("고딩A"));

// 2. 학생 반 : 학생만 수강등록이 가능한 코스

Course<Student> 학생반 = new Course<>("학생과정", 10);

// 학생반.add(new Person("일반인A"));

// 학생반.add(new Worker("직장인A"));

학생반.add(new Student("학생A"));

학생반.add(new HighStudent("고딩A"));

// 3. 직장인 반: 직장인만 수강등록이 가능한 코스

Course<Worker> 직장인반 = new Course<>("직장인과정", 5);

// 직장인반.add(new Person("일반인A"));

직장인반.add(new Worker("직장인A"));

// 직장인반.add(new Student("학생A"));

// 직장인반.add(new HighStudent("고딩A"));

 

// 메서드

// A. 일반인

System.out.println("-----일반인과정-----");

일반인(일반인반);

일반인(학생반);

일반인(직장인반);

System.out.println();

// B. 학생

System.out.println("-----학생과정-----");

//학생(일반인반);

학생(학생반);

//학생(직장인반);

System.out.println();

 

// C. 직장인

System.out.println("-----직장인과정-----");

직장인(일반인반);

//직장인(학생반);

직장인(직장인반);

System.out.println();

}

// 수강등록하기

// 1. 일반인

public static void 일반인(Course<?> course) {

System.out.println(course.getName()+"수강생: "+Arrays.toString(course.getStudents()));

}

// 2. 학생

public static void 학생(Course<? extends Student> course) {

System.out.println(course.getName()+"수강생: "+Arrays.toString(course.getStudents()));

 

}

// 3. 직장인

public static void 직장인(Course<? super Worker> course) {

System.out.println(course.getName()+"수강생: "+Arrays.toString(course.getStudents()));

 

}

}

/*

제네릭타입의 상속(extends)과 구현(implement)

제네릭타입도 다른 타입과 마찬가지로 부모클래스가 될 수 있다.

상속받은 자식클래스는 부모클래스에 정의된 타입을 정의해야 한다.

 

또한, 자식클래스는 타입파라미터를 추가할 수 있으며 제네릭 인터페이스를 구현한

클래스에서도 제네릭타입으로 정의되어야 한다.

 

*/

public class Product<K, M> {

private K kind;

private M model;

 

public K getKind() {

return kind;

}

public void setKind(K kind) {

this.kind = kind;

}

public M getModel() {

return model;

}

public void setModel(M model) {

this.model = model;

}

@Override

public String toString() {

return "Product [kind=" + kind.getClass() + ", model=" + model + "]";

}

}

class Car{}

class TV{}

public class ChildProduct<K, M, C> extends Product<K, M> {

private String Company;

 

public String getCompany() {

return Company;

}

 

public void setCompany(String company) {

this.Company = company;

}

 

}

public interface Storage<T> {

void add(T item, int index);

T get(int index);

}

public class Storageimpl <T> implements Storage<T>{

 

private T[] array;

 

public Storageimpl(int capacity) {

this.array = (T[])new Object[capacity];

}

@Override

public void add(T item, int index) {

array[index] = item;

}

 

@Override

public T get(int index) {

return array[index];

}

 

}

public class ExtendsMain {

 

public static void main(String[] args) {

// 1. 클래스(Product)

ChildProduct<TV, String, String> tv = new ChildProduct<>();

tv.setKind(new TV()); // 부모의 제네릭타입

tv.setModel("Smart OLED TV"); // 부모의 제네릭타입

tv.setCompany("LG 전자"); // 자식의 제네릭타입

System.out.println(tv.toString());

 

ChildProduct<Car, String, String> car = new ChildProduct<>();

car.setKind(new Car());

car.setModel("911");

car.setCompany("포르쉐");

System.out.println(car.toString());

System.out.println();

 

// 2. 인터페이스(Storage)

Storage<TV> tv창고 = new Storageimpl<>(100);

tv창고.add(new TV(), 10); // 10번위치

TV tv1 = tv창고.get(10);

System.out.println(tv1.getClass());

 

Storage<Car> 자동차창고 = new Storageimpl<>(10);

자동차창고.add(new Car(),1); // 1번위치

Car car1 = 자동차창고.get(1);

System.out.println(car1.getClass());

}

}

' > JAVA' 카테고리의 다른 글

java15.collection  (0) 2023.05.26
java14.lambda 람다람다  (0) 2023.05.26
java12.thread  (0) 2023.05.26
java11.basic_API.time  (0) 2023.05.25
java11.basic_API.string  (0) 2023.05.25