반응형
01. 표현대상을 추상화하여 클래스로 선언할 때 주의점
1. 메소드호출이 다르다면?
- 무조건 클래스 따로 만들어줘야함
2. 메소드 호출이 동일하지만, 필드의 자료형만 다르다면?
public 클래스1 {
String A;
동일한 기능의 메소드;
}
public 클래스2 {
int A;
동일한 기능의 메소드;
}
public 클래스3 {
double A;
동일한 기능의 메소드;
}
각각의 클래스들을 만드는 것은 매우 비효율적!
클래스를 여러개 만들지 말고, 필드를 Object 자료형으로 선언해보자!
public 클래스 {
Object A;
동일한 기능의 메소드;
}
필드의 자료형이 Object이기 때문에 어떤 클래스로 생성되든 모든 객체 저장해 사용 가능
- 객체 저장 ok
- 만약 원시형데이터타입을 저장하고 싶다? wrapper객체들 이용해서, Wrapper클래스의 오토박싱에 의해 기본값을 저장하면 객체로 변환되어 저장됨
02. Non-Generic클래스
1. NonGeneric.java
package xyz.itwill.util;
//Non-Generic클래스
// => Object클래스 타입의 필드가 선언되어 있음 - 즉, 모든 클래스의 객체를 전달하여 저장이 가능함
// => 표현대상을 추상화하여 클래스로 선언할 경우 필드의 자료형에 따라 여러개의 클래스를 선언하여 사용하는 경우 발생
// => 필드의 자료형을 Object 클래스로 선언하면 필드에는 모든 클래스로 생성된 객체를 저장 가능
public class NonGeneric {
//필드
private Object field;
//생성자
public NonGeneric() { }
public NonGeneric(Object field) { super(); this.field = field;}
//Getter & Setter
public Object getField() {//무엇이든 반환하는 메소드
return field;}
public void setField(Object field) { //무엇이든 전달가능한 메소드
this.field = field;}
}
2. NonGenericApp.java
package xyz.itwill.util;
//Object 클래스로 필드가 선언된 클래스로 객체를 생성하여 사용하는 프로그램
// => Object 클래스의 필드에 모든 클래스의 객체를 전달하여 저장 가능
// => Object 클래스의 필드에 저장된 객체를 반환받아 사용할 경우 반드시 명시적 객체 형변환 후 사용
public class NonGenericApp {
public static void main(String[] args) {
//🥝 Non-Generic 클래스 사용 시 장점
// => 1. 어떤 객체든 필드에 저장 가능
NonGeneric nonGeneric1 = new NonGeneric();
NonGeneric nonGeneric2 = new NonGeneric();
NonGeneric nonGeneric3 = new NonGeneric();
//Integer 객체를 Object 클래스의 매개변수로 전달받아 Object 클래스의 필드값으로 변경
nonGeneric1.setField(100); //오토박싱 되어 정수값100이 Integer객체로 변환되어 저장됨
//Double 객체를 Object 클래스의 매개변수로 전달받아 Object 클래스의 필드값으로 변경
nonGeneric2.setField(12.34); //오토박싱 되어 실수값12.34가 Double객체로 변환되어 저장됨
//String 객체를 Object 클래스의 매개변수로 전달받아 Object 클래스의 필드값으로 변경
nonGeneric3.setField("홍길동");
//🥝Non-Generic클래스 사용 시 단점
// => 1. 오직 Object클래스의 메소드만 사용가능
nonGeneric1.getField().toString(); //Object가 가진 toString()메소드호출
nonGeneric1.getField().equals(nonGeneric3);//Object가 가진 equals(Object obj)메소드호출
nonGeneric1.getField().hashCode();//Object가 가진 hashCode()메소드호출
// => 2. 필드값을 변경하기 위해 명시적 객체 형변환을 사용하면 자식클래스의 메소드도 호출할 수 있지만!!!!!!!!!!!
((Integer)nonGeneric1.getField()).intValue(); //Integer가 가진 intValue()메소드호출
((Double)nonGeneric2.getField()).doubleValue(); //Double이 가진 doubleValue()메소드호출
((String)nonGeneric3.getField()).charAt(1); //String이 가진 charAt(int index)메소드호출
Integer returnObject1=(Integer)nonGeneric1.getField();
System.out.println("필드값 = "+returnObject1); //오토언박싱, intValue()생략가능
System.out.println("필드값 = "+returnObject1.intValue());
System.out.println("hashCode() = "+returnObject1.hashCode());
//필드값 = 100
//필드값 = 100
//hashCode() = 100
Double returnObject2=(Double)nonGeneric2.getField();
System.out.println("필드값 = "+returnObject2); //오토언박싱, doubleValue()생략가능
System.out.println("필드값 = "+returnObject2.doubleValue());
System.out.println("hashCode() = "+returnObject2.hashCode());
//필드값 = 12.34
//필드값 = 12.34
//hashCode() = 986311098
String returnObject3=(String)nonGeneric3.getField();
System.out.println("필드값 = "+returnObject3);
System.out.println("hashCode() = "+returnObject3.hashCode());
//필드값 = 홍길동
//hashCode() = 54150062
// => 3. 명시적 객체 형변환을 잘못할 경우 ClassCastException 발생할 수 있으므로 instanceof 연산자를 사용하여 명시적 객체 형변환 전에 검증 해주어야 함!
//Integer returnObject2=(Integer)nonGeneric2.getField(); //ClassCastException발생
if(nonGeneric2.getField() instanceof Double) {
Double returnObject2=(Double)nonGeneric2.getField();
System.out.println("필드값 = "+returnObject2);//오토언박싱
}
// => 4. 가장 치명적인 단점은 무조건 객체 형변환을 통해 자식객체의 메소드를 호출할 수 있으며,
// => ClassCastException이 발생될 수 있으므로 반드시 예외처리 해주어야함ㅠㅠ
// => 5. 마지막으로, 예외처리 시 try-catch로 메세지 출력해야하는데,
// => 문제는 Object는 모든 클래스의 부모라 자식이 너무 많아출력할 메세지를 모든클래스에 해줄 수 없다는 것!!!!ㅜㅜ
//🥝해결방법 - Generic 클래스를 이용하자!
}
}
3. 알고 넘어가기 (Non제네릭 - 비권장)
- 자료구조는 대부분 제네릭클래스지만, 사실 Non제네릭클래스로 만들어도 된다.
- 하지만 제네릭을 사용하지 않고 객체를 생성하는 것은 비권장이다.
- 예를들어, HashSet객체를 nongeneric으로 생성한다면, HashSet객체에 모든 클래스의 객체가 Object 타입으로 저장되기 때문이다.
- 즉, 어떤 객체든지 저장 가능하다는 것
- 또한 HashSet 객체에 저장된 객체를 반환받아 사용할 경우, 반드시 명시적 객체 형변환 후 사용 가능하다.
//Non-Generic
//제네릭을 사용하지 않고 HashSet객체 생성 - 비권장
//=> HashSet객체에 모든 클래스의 객체가 Object타입으로 저장됨
//HashSet 객체에 저장된 객체를 반환받아 사용할 경우
//=>반드시 명시적 객체 형변환 후 사용가능
HashSet set = new HashSet(); //비권장
//배열처럼 어떤 객체든 여러개 저장 가능~ (타입:Object)
set.add(100);
set.add(12.34);
set.add("홍길동");
03. Generic클래스
1. 제네릭(Generic)이란?
- 필드의 자료형(참조형) 대신 사용될 식별자
- 미지정 자료형
2. 제네릭 클래스란?
- 제네릭을 선언한 클래스
- 즉, 제네릭을 필드의 자료형으로 사용 가능한 클래스
3. Generic클래스 만드는 방법
- 자바의 참조형(클래스/인터페이스/이넘) 선언 시 < > 기호에 제네릭 타입(식별자) 선언하기
- < > 안에 제네릭 타입은 , 기호를 사용하여 나열 선언 가능
- 제네릭 타입의 식별자는 대문자로만 작성하는 것을 권장(스네이크 표기법) : ex) T
- 제네릭으로 전달받을 수 있는 클래스를 상속을 사용하여 제한 가능
public class 클래스명 <제네릭, 제네릭, 제네릭...>{ }
public interface 인터페이스명 <제네릭, 제네릭, 제네릭...>{ }
public enum 이넘명 <제네릭, 제네릭, 제네릭...>{ }
//상속받는 제네릭 - 제한설정
// => 제네릭은 반드시 부모클래스를 상속받은 자식클래스만 전달받아 사용 가능
public class 클래스명 <제네릭 extends 부모클래스> { }
//제네릭을 이용한 객체생성 시 Number를 상속받지 않는 자식클래스 생성 불가능 ex.String
public class Generic /*<T>*/ <T extends Number>{ }
4. Generic.java
package xyz.itwill.util;
//제네릭(Generic)클래스
public class Generic<T extends Number> {
private T field;
public Generic() {
// TODO Auto-generated constructor stub
}
public Generic(T field) {
super();
this.field = field;
}
public T getField() {
return field;
}
public void setField(T field) {
this.field = field;
}
}
5. 🍯 Generic클래스 사용꿀팁
- 제네릭명은 중요하지 않음 - <E> <V> <K> ..
- 만약 제네릭 클래스의 제네릭이 두개라면? - <K , V> : 2개의 클래스를 전달해야함
- 자료구조는 Object타입보다 제네릭이 훨씬 좋음 - 자료구조 = 제네릭 권장
- 특히 자료구조와 관련된 클래스와 인터페이스는? 대부분 제네릭클래스로 만들어져있음
- 제네릭 대신 표현할 클래스를 전달하여, 해당 클래스 타입의 객체 생성 or 참조변수생성 or 메소드 호출 잘 하는 것이 중요
- 이 밖에도 배포된 클래스에는?
- 제네릭을 가진 클래스가 많음(Generic)
- 물론 Object필드만 가진 클래스도 많음(non-Generic)
- 우리가 직접 제네릭클래스를 만들일은 없음 대신 배포된 제네릭클래스를 잘 활용할 수 있으면 됨!
6. GenericApp.java
package xyz.itwill.util;
//제네릭 클래스로 객체를 생성하여 사용하는 프로그램
// => 제네릭 대신 설정된 클래스의 객체만 필드에 저장 가능 - 제한적인 객체 저장
// => 필드값을 반환받아 사용할 때 명시적 객체 형변환 불필요
//🍓Generic클래스 사용 시 장점
// => 제네릭으로 클래스를 설정만해주면 원하는 클래스의 객체들을 필드에 저장 가능
// => 필드값을 반환받아 사용할 때 명시적 객체 형변환이 불필요 - 왜? 제한적인 객체 저장했기 때문
// => 이러한 장점으로 특히 자료구조클래스에서 많이 이용 - 왜? 객체를 여러개 저장해야하기 때문에 제네릭을 같이 사용함
public class GenericApp {
public static void main(String[] args) {
//1.
//제네릭 이용하지 않고 객체생성 가능
Generic generic = new Generic(); //ok
//2.
//제네릭을 이용한 객체 생성
//제네릭을 이용할 경우 제네릭 대신 사용할 클래스 전달 필수 - 전달받은 클래스는 제네릭 대신 사용되어 필드 또는 메소드 완성
//=>앞으로 <T>는 <Integer>로 변경되어 사용된다.
//=> 즉, field의 자료형 = Integer클래스 - getField & setField의 자료형 = Integer클래스
Generic<Integer> generic1 = new Generic<Integer>();
Generic<Double> generic2 = new Generic<Double>();
Generic<String> generic3 = new Generic<String>();
//3.
//필드값 세팅 - 제네릭으로 설정된 클래스와 동일한 자료형의 객체를 전달하여 필드에 저장
// => 제네릭으로 설정된 클래스와 다른 자료형의 객체를 전달할 경우 에러 발생
//만약, 다른 자료형의 객체를 전달할 경우 에러 발생 - 필드에 제한적인 객체 저장
generic1.setField(100); //오토박싱
//generic1.setField(12.34); //error
generic2.setField(12.34); //오토박싱
generic3.setField("홍길동");
//4.
//필드값 출력
Integer returnObeject1 = generic1.getField();
Double returnObeject2 = generic2.getField();
System.out.println("필드값 = "+returnObeject1); //필드값 = 100
System.out.println("필드값 = "+returnObeject2); //필드값 = 12.34
//5. 메소드호출 - 명시적 객체 형변환없이 이용가능
generic1.getField().intValue();
generic2.getField().doubleValue();
generic3.getField().length();
//6. 상속받은 제네릭
// 만약, public class Generic /*<T>*/ <T extends Number>{ } 라면
//제네릭의 부모클래스를 상속받지 않은 자식클래스로 제네릭클래스를 이용하면 에러 발생
/*
Generic<String> generic3 = new Generic<String>(); //error
generic3.setField("홍길동");
String returnObject3=generic3.getField();
System.out.println("필드값 = "+returnObject3);
*/
}
}
반응형
'java > java.util & java.text' 카테고리의 다른 글
[java.util] 6. HashSet 클래스 & ArrayList 클래스 & HashMap 클래스 (0) | 2024.06.27 |
---|---|
[java.util] 5. Collection(자료구조)클래스 기초 (0) | 2024.06.27 |
[java.text] 3. DecimalFormat 클래스 & SimpleDateFormat 클래스 (0) | 2024.06.26 |
[java.util] 2. Date 클래스 & Calendar 클래스 (0) | 2024.06.18 |
[java.util] 1. Random 클래스 & UUID 클래스 (0) | 2024.06.18 |