원문 번역중입니다.
원문 : http://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html


Iterating over a collection is uglier than it needs to be. Consider the following method, which takes a collection of timer tasks and cancels them:
void cancelAll(Collection<TimerTask> c) {
for (Iterator<TimerTask> i = c.iterator(); i.hasNext(); )
i.next().cancel();
}

The iterator is just clutter. Furthermore, it is an opportunity for error. The iterator variable occurs three times in each loop: that is two chances to get it wrong. The for-each construct gets rid of the clutter and the opportunity for error. Here is how the example looks with the for-each construct:

void cancelAll(Collection<TimerTask> c) {
for (TimerTask t : c)
t.cancel();
}

When you see the colon (:) read it as “in.” The loop above reads as “for each TimerTask t in c.” As you can see, the for-each construct combines beautifully with generics. It preserves all of the type safety, while removing the remaining clutter. Because you don't have to declare the iterator, you don't have to provide a generic declaration for it. (The compiler does this for you behind your back, but you need not concern yourself with it.)

Here is a common mistake people make when they are trying to do nested iteration over two collections:

List suits = ...;
List ranks = ...;
List sortedDeck = new ArrayList();

// BROKEN - throws NoSuchElementException!
for (Iterator i = suits.iterator(); i.hasNext(); )
for (Iterator j = ranks.iterator(); j.hasNext(); )
sortedDeck.add(new Card(i.next(), j.next()));

Can you spot the bug? Don't feel bad if you can't. Many expert programmers have made this mistake at one time or another. The problem is that the next method is being called too many times on the “outer” collection (suits). It is being called in the inner loop for both the outer and inner collections, which is wrong. In order to fix it, you have to add a variable in the scope of the outer loop to hold the suit:

// Fixed, though a bit ugly
for (Iterator i = suits.iterator(); i.hasNext(); ) {
Suit suit = (Suit) i.next();
for (Iterator j = ranks.iterator(); j.hasNext(); )
sortedDeck.add(new Card(suit, j.next()));
}

So what does all this have to do with the for-each construct? It is tailor-made for nested iteration! Feast your eyes:

for (Suit suit : suits)
for (Rank rank : ranks)
sortedDeck.add(new Card(suit, rank));

The for-each construct is also applicable to arrays, where it hides the index variable rather than the iterator. The following method returns the sum of the values in an int array:

// Returns the sum of the elements of a
int sum(int[] a) {
int result = 0;
for (int i : a)
result += i;
return result;
}
So when should you use the for-each loop? Any time you can. It really beautifies your code. Unfortunately, you cannot use it everywhere. Consider, for example, the expurgate method. The program needs access to the iterator in order to remove the current element. The for-each loop hides the iterator, so you cannot call remove. Therefore, the for-each loop is not usable for filtering. Similarly it is not usable for loops where you need to replace elements in a list or array as you traverse it. Finally, it is not usable for loops that must iterate over multiple collections in parallel. These shortcomings were known by the designers, who made a conscious decision to go with a clean, simple construct that would cover the great majority of cases.

원문 번역중입니다.

원문 : http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html


당신이 Cellction 에서 element를 취할 때, 당신은 Collection 안에 저장되어있는 element로 타입을 캐스팅해야만 한다. 게다가 더 불편한것은, 이것은 위험하다는 것이다. 컴파일러는 당신이 Collection 타입을 동일하게 캐스팅했는데 체크하지 않으며, 따라서 런타임시에 캐스팅은 실패할 수 있다.

Generics 은 당신이 컴파일러에게 Collection의 타입에 대해 전할 수 있는 방법을 제공한다. 따라서 이것은 컴파일러에 의해 체크될 것이다. 한번에 컴파일러는 Collection의 타입을 알며, 당신이 Collection에 저장하거나 취할 때 올바른 캐스팅을 하고 있는지 체크할 수 있다.


여기에 Collection 튜토리얼로부터 얻은 간단한 예제가 있다.

// Removes 4-letter words from c. Elements must be strings
static void expurgate(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); )
if (((String) i.next()).length() == 4)
i.remove();
}


여기에 generic을 사용한 수정된 예제가 있다.

// Removes the 4-letter words from c
static void expurgate(Collection<String> c) {
for (Iterator<String> i = c.iterator(); i.hasNext(); )
if (i.next().length() == 4)
i.remove();
}

당신이 <Type> 코드를 보게 될 때, 이것은 "of Type"으로 읽는다. 위에 정의는 "Collection of String c"로 읽는다. generics를 사용한 코드는 더 깨끗하며 더 안전한다. We have eliminated an unsafe cast and a number of extra parentheses. More importantly, we have moved part of the specification of the method from a comment to its signature, so the compiler can verify at compile time that the type constraints are not violated at run time. Because the program compiles without warnings, we can state with certainty that it will not throw a ClassCastException at run time. The net effect of using generics, especially in large programs, is improved readability and robustness.

To paraphrase Generics Specification Lead Gilad Bracha, when we declare c to be of type Collection<String>, this tells us something about the variable c that holds true wherever and whenever it is used, and the compiler guarantees it (assuming the program compiles without warnings). A cast, on the other hand, tells us something the programmer thinks is true at a single point in the code, and the VM checks whether the programmer is right only at run time.

generics의 주된 사용은 Collection 이지만, 여기에는 다른 많은 사용법이 있다. “Holder classes,” such as WeakReference and ThreadLocal, have all been generified, that is, they have been retrofitted to make use of generics. More surprisingly, class Class has been generified. Class literals now function as type tokens, providing both run-time and compile-time type information. This enables a style of static factories exemplified by the getAnnotation method in the new AnnotatedElement interface:

    <T extends Annotation> T getAnnotation(Class<T> annotationType); 
This is a generic method. It infers the value of its type parameter T from its argument, and returns an appropriate instance of T, as illustrated by the following snippet:
    Author a = Othello.class.getAnnotation(Author.class);
Prior to generics, you would have had to cast the result to Author. Also you would have had no way to make the compiler check that the actual parameter represented a subclass of Annotation.

Generics are implemented by type erasure: generic type information is present only at compile time, after which it is erased by the compiler. The main advantage of this approach is that it provides total interoperability between generic code and legacy code that uses non-parameterized types (which are technically known as raw types). The main disadvantages are that parameter type information is not available at run time, and that automatically generated casts may fail when interoperating with ill-behaved legacy code. There is, however, a way to achieve guaranteed run-time type safety for generic collections even when interoperating with ill-behaved legacy code.

The java.util.Collections class has been outfitted with wrapper classes that provide guaranteed run-time type safety. They are similar in structure to the synchronized and unmodifiable wrappers. These “checked collection wrappers” are very useful for debugging. Suppose you have a set of strings, s, into which some legacy code is mysteriously inserting an integer. Without the wrapper, you will not find out about the problem until you read the problem element from the set, and an automatically generated cast to String fails. At this point, it is too late to determine the source of the problem. If, however, you replace the declaration:

    Set<String> s = new HashSet<String>();
with this declaration:
    Set<String> s = Collections.checkedSet(new HashSet<String>(), String.class);
the collection will throw a ClassCastException at the point where the legacy code attempts to insert the integer. The resulting stack trace will allow you to diagnose and repair the problem.

당신이 할 수 있는 모든 곳에서 generics를 사용할 수 있다. The extra effort in generifying code is well worth the gains in clarity and type safety. It is straightforward to use a generic library, but it requires some expertise to write a generic library, or to generify an existing library. There is one caveat: You may not use generics (or any other Tiger features) if you intend to deploy the compiled code on a pre-5.0 virtual machine.

If you are familiar with C++'s template mechanism, you might think that generics are similar, but the similarity is superficial. Generics do not generate a new class for each specialization, nor do they permit “template metaprogramming.”

generics 에 대해 좀 더 많은걸 배우고 싶다면  Generics Tutorial 여기를 보라.

+ Recent posts