반응형
반응형
Generic (제너릭)
 
 
What ?
 
제너릭은 컬렉션(자료구조),
 
즉 쉽게 말해서 객체들을 저장(수집)하는
 
구조적인 성격을 보강하기 위해 제공되는 것.
 
 

0. GENERIC

다들 아시겠지만, Generic의 사전적인 의미는 다음과 같습니다.


 1.     (생물) 속의 , (성질) 속에 특이한

 2.     일반적인, 포괄적인

 3.     (수,인칭,시제) 총칭적인



1. generic 필요성

Java 에서는 객체들을 담아 편하게 관리하기 위해  Collection 을 제공한다. 이 Collection 의 대부분이

어떤 객체를 담을지 모르기 때문에 모든 자바 객체들의 base 객체 (최상위 객체)인 Object로

저장되어 설계하도록 설계되어 있습니다.

문제는 이 Collection 에 Element로 어떤 Type을 받아 들임이 좋을수도 있지만

서로 다른 Type이 하나의 Collection 에 섞여 들어 가는것이 문제 JDK 5.0 에 와서 제너릭이 포함되면서

이제는 실행하기 전에 컴파일 단계에서 특정 Collection 에 객체 타입을 명시하여

지정된 객체가 아니면 절대 저장이 불가능하게 할수 있습니다.

2. 제너릭의 타입

제너릭 타입은 < > 사이에 컴파일할 당시 사용될 객체자료형만 선언 해주면 객체를 저장할 때

선언된 제너릭 타입으로만 저장된다.

API 에서는 전달되는 객체가 현 객체 내에서 하나의 자료형(Type)으로 쓰일 때 <T> 로 유도하고 있으며

전달되는 객체가 현 객체 내에서 하나의 요소(Element)로 자리를 잡을때는 <E> 로 , 그리고 전달되는 객체가

현 객체 내에서 Key 값으로 사용될 때는 <K>로,

만약 전달되는 객체가 현 객체 내에서 Value 값으로 사용될 때는 <V> 로 표현된다.

3. 사용자 정의 제네릭 클래스  


앞에서 언급한 자료형을 제너릭으로 set 하고 print 해주는 간단한 사용자 정의 클래스를 만들어 보겠습니다.

/*
* FILE NAME  : Generic.java
* DATE   : 2007. 8. 5
* Written By  : yangck20@naver.com
*
*/
package ckbin.array;
public class Generic<T> {
 T[] v;
 
// Generic 타입으로 Set
 public void set(T[] n) {
  v = n;
 }
 
// T[] 원소 출력
 public void print() {
  for(T s : v)
   System.out.println(s);
 }

}




 

 
사용자_Class명 <적용할_제너릭 타입> 변수명;   // 선언
 
 
변수명 = new 사용자_Class명<적용할_제너릭 타입>( ) ; // 생성
 



4. 제너릭 타입 사용하기


사용자 정의형으로 만든 제너릭 클래스의 사용

 




위에서 생성한 사용자_Class를 사용하여 생성할때는 다음과 같이 생성자를 만들수 있습니다.

Generic<String> Gen = new Generic<String> ();

 위에서 생성한 사용자_Class 를 사용하는 Main Class 를 만들어 보겠습니다.

 /*
* FILE NAME  : GenericMain.java
* DATE   : 2007. 8. 5
* Written By  : yangck20@naver.com
*
*/
package ckbin.array;
import ckbin.array.Generic;
public class GenericMain {
 
 public static void main(String ar[]) {
 
  Generic<String> Gen = new Generic<String> ();
 
  String[] StrArr = {"디워","정말","보고파"};
 
  Gen.set(StrArr);
  Gen.print();

 
  Generic<Integer> Gen2 = new Generic<Integer> ();
 
  Integer[] IntArr = {2,0,0};
 
  Gen2.set(IntArr);
  Gen2.print();

 
 }
}
 
== 출력 결과 =============================================================================
디워
정말
보고파
2
0
0
 

 



















위 GenericMain 소스를 보면 Generic 클래스는 String 배열과

Integer 배열을 받아 출력해주는 아주 간단한 소스지만 타입을

달리주어 객체에 담은 배열들을 출력해주는것이 이상적입니다.

HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("1", new Integer(1));
map.put("2", 2);            // auto boxing
 
Integer i = map.get("1");   // 따로 캐스팅이 필요 없다.
int j = map.get("2");       // auto unboxing


이렇게 별도의 캐스팅 없이 처리할 수 있다는 점에서 좋아졌네요..

반응형

J2SE 5.0에는 새로운 언어 기능이 다수 도입되었는데, 여기에는 제네릭(Generics)과 향상된 루프문(Enhanced for Loop)이 포함된다. 제네릭(GENERIC)향상된 루프문에 대해서는 이미 이전 테크 팁에서 살펴본 바 있다. J2SE 5.0에 추가된 또 다른 중요한 기능으로 주석을 들 수 있으며, 본 팁에서는 J2SE 5.0에 내장된 주석을 살펴보기로 한다.

먼저, 주석이란 무엇인가? JSR 175: A Metadata Facility for the Java Programming Language의 일부로 정의된 주석은 메타데이터를 프로그램 엘리먼트(클래스, 인터페이스, 메소드 등)에 연결하는 방법을 제시해준다. 주석은 해당 엘리먼트에 대해 생성된 바이트코드를 변경하지 않는 추가 수식자(modifier)라고 할 수 있다.

메타데이터를 소스 코드에 도입한다는 개념은 J2SE 5.0에서 처음 등장한 것은 아니다. 사용자가 @deprecated 태그를 메소드의 javadoc 코멘트에 추가하면 컴파일러는 이를 메소드에 관한 메타데이터로 취급하는데, 이런 기능은 J2SE의 1.0 버전부터 포함되었다. 초기 버전의 플랫폼의 경우 이미 Systemgetenv() 메소드(1.1 addendum까지는 Java Language Specification에 포함되어 있지 않았음)와 더불어 구(deprecated) 메소드가 포함되어 있었지만, 현재로서는--적어도 구문의 @ 부분에 관한 한--개념은 거의 동일하다고 볼 수 있다. 단, 위치에는 변동 사항이 생겼다는 점을 알아두기 바란다--주석 태그가 코멘트가 아니라 소스에 포함된다. 여기서 주안점은 주석이 선언적 프로그래밍 모델을 체계적으로 지원하기 위한 방법이라는 사실이다.

이제 J2SE 5.0에 포함된 최초의 주석, @Deprecated부터 살펴보도록 하자. 우선 여기서는 대문자 D에 유의할 필요가 있다. 기능적으로 볼 때, 소스 내의 @Deprecated는 클래스 또는 메소드와 연결된 javadoc 내의 @deprecated와 동일하게 작동한다. 메소드에 @Deprecated 태그를 플래그하면 해당 메소드나 클래스 사용 시 사용자에게 경고 메시지를 보내도록 컴파일러를 환기시키는 효과가 있다.

아래의 Main 클래스는 @Deprecated 주석과 @deprecated 코멘트가 플래그된 deprecatedMethod()라는 이름의 메소드를 가진다.

  public class Main {

/**
* @deprecated Out of date. Use System.foobar() instead.
*/

@Deprecated
public static void deprecatedMethod() {
System.out.println("Don't call me");
}
}

주석이 붙은 클래스도 주석이 붙지 않은 경우와 동일한 방식으로 컴파일한다.

> javac Main.java

예상대로 Main.class가 생성된다.

deprecated 메소드를 사용하면 javadoc 내의 @deprecated 태그를 사용할 때와 마찬가지로 컴파일 시간 경고가 생성된다. 관련 예는 다음과 같다.

  public class User {
public static void main(String args[]) {
Main.deprecatedMethod();
}
}

아래의 클래스를 컴파일하면,

> javac User.java

deprecated 메소드를 사용하는 데 대한 경고 메시지가 다음과 같이 표시된다.

  Note: User.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

컴파일 라인에 -Xlint를 추가하면 무엇이 잘못되었는지 구체적으로 표시할 수 있다.

> javac -Xlint:deprecation User.java

User.java:3: warning: [deprecation] deprecatedMethod() in
Main has been deprecated
Main.deprecatedMethod();
^

1 warning

@deprecated 코멘트에서 @Deprecated 주석으로 변경되더라도 시스템에는 아무런 변화가 생기지 않으며, 단지 작업 수행 방식에 약간의 변화가 발생하는 것 뿐이다. 하지만 J2SE 5.0 플랫폼에 새로 추가된 다른 두 개의 주석, @Override@SuppressWarnings의 경우에는 플랫폼에 새로운 기능을 부여할 수 있다.

@Override 주석은 메소드 선언과 함께 사용할 수 있는데, 이름에서 알 수 있듯이 @Override 주석을 사용하여 수퍼클래스의 메소드를 오버라이드하도록 되어 있는 메소드를 플래그한다. 이 주석은 왜 사용하는 것일까? 오류를 더 신속하게 잡아내기 위해서이다. 아마도 여러분은 메소드를 오버라이드하려다가 메소드 이름의 철자를 틀리거나, 잘못된 인자를 지정하거나, 다른 리턴 타입을 설정했던 적이 무수히 많았을 것이다. 즉, 원래의 의도는 기존의 메소드를 오버라이드하는 것이었는데 그만 새로운 메소드를 정의해 버리는 결과가 종종 발생하게 되는 것이다. @Override를 이용하면 그나마 클래스에서 문제점을 빨리 발견할 수가 있다.

  public class Overrode {
@Override
public int hashcode() {
return 0;
}

@Override
public boolean equals(Object o) {
return true;
}
}

여기서 문제는 메소드 이름이 hashcode가 아니라 hashCode여야 한다는 점이다. 메소드 선언이 훨씬 더 큰 클래스 정의를 위해 소스에 묻혀 버린다고 가정해보자. 최초의 @Override 주석이 없다면, hashCode() 메소드(모두 소문자가 아니라, 대문자로 시작되는 두개의 영단어를 붙여 써서)가 호출되는 대신 상위 Object 클래스의 기본값 동작을 얻고 있다는 사실을 알아차리는 데 얼마나 많은 시간이 걸리겠는가? 하지만 이제는 @Override 주석 덕분에, 클래스 컴파일 시 컴파일 시간 오류가 생성되어 문제점을 경고할 수 있다.

> javac Overrode.java

Overrode.java:2: method does not override a method from its
superclass
@Override
^
1 error

이런 특성의 오류를 좀더 빨리 발견할 수만 있다면 수정에 드는 수고와 비용을 획기적으로 절감할 수 있을 것이다. hashCode() 메소드의 경우 절대로 상수를 리턴해서는 안된다는 점에 유의할 것. hashCode()equals()의 올바른 사용법에 관한 자세한 설명을 보려면 Joshua Bloch가 저술한 Effective Java Programming Language Guide(영문)의 8항을 참조할 것.

J2SE 5.0에 새로 추가된 주석의 마지막 @SuppressWarnings는 그 중에서도 가장 흥미롭다고 할 수 있다. 이 주석은 일반적으로 경고하는 내용을 경고하지 말도록 컴파일러에게 지시하는데, 경고는 일종의 범주에 속하므로 주석에 대해 어떤 종류의 경고를 금지할 것인지 지시해야 한다. javac 컴파일러는 all, deprecation, unchecked, fallthrough, path, serial, finally 등 7개의 금지 옵션을 정의한다. (언어 스펙은 이 중에서 두 가지-- deprecation과 unchecked--만을 정의함.)

예시를 위해 fallthrough 옵션 금지사항에 대해 살펴보기로 한다. 먼저 아래의 클래스를 사용해보자. 클래스에는 switch 문의 각 케이스에 대한 break 문이 빠져 있다는 점에 유의할 것.

  public class Fall {
public static void main(String args[]) {
int i = args.length;
switch (i) {
case 0: System.out.println("0");
case 1: System.out.println("1");
case 2: System.out.println("2");
case 3: System.out.println("3");
default: System.out.println("Default");
}
}
}

javac으로 클래스를 컴파일하면 단순히 .class 파일만 생성되고 경고는 표시되지 않는다는 것을 알 수 있다.

javac Fall.java

컴파일러가 fall through하는(즉, 하나 이상의 break 문이 빠져 있는) switch 문에 관해 경고하기를 원할 경우에는 -Xlint:fallthrough 옵션으로 컴파일한다.

javac -Xlint:fallthrough Fall.java

그러면 아래의 경고문이 생성된다.

Fall.java:6: warning: [fallthrough] possible fall-through into case
case 1: System.out.println("1");
^
Fall.java:7: warning: [fallthrough] possible fall-through into case
case 2: System.out.println("2");
^
Fall.java:8: warning: [fallthrough] possible fall-through into case
case 3: System.out.println("3");
^
Fall.java:9: warning: [fallthrough] possible fall-through into case
default : System.out.println("Default");
^
4 warnings

그러나 switch 문에 각 케이스에 대한 break 문이 빠져 있다는 사실을 무시하고 싶은 경우에는 어떻게 해야 할까? 바로 이 때 @SuppressWarnings 주석이 필요하다. main() 메소드 선언 앞에 다음 행을 추가하면,

@SuppressWarnings("fallthrough")

-Xlint:fallthrough 옵션으로 클래스를 컴파일할 경우,

   javac -Xlint:fallthrough Fall.java

.class 파일만 생성되고 경고문은 표시되지 않는다.

@SuppressWarnings 주석은 또한 컬렉션 엘리먼트의 데이터 유형을 지정하지 않고 컬렉션을 사용할 경우에 표시되는 것과 같은 그 밖의 경고를 금지하는 데도 사용될 수 있다. 그러나 단순히 컴파일 시간 경고를 피하기 위해서 @SuppressWarnings 주석을 사용해서는 안 된다. 제네릭을 염두에 두지 않고 구축된 라이브러리를 사용할 때처럼, 금지되지 않은 경고가 불가피한 경우에 이 주석을 사용하도록 한다.

이것으로 내장 주석 기능에 관한 설명은 마무리하고, 마지막으로 (인자를 포함한) 주석은 통상적으로 하나의 행에서 독자적으로 지정된다는 점에 유의하기 바란다.

J2SE 5.0에 이미 정의되어 있는 주석을 사용하는 것 보다 사용자가 직접 주석을 정의하는 경우에는 더 많은 기능들을 활용할 수 있다. 주석 정의에 관한 자세한 내용은 Annotations(영문)를 참조하기 바란다.

<출처 - http://kr.sun.com/developers/techtips/2006/c0713.html >

+ Recent posts