Сообщений 1    Оценка 34        Оценить  
Система Orphus

Java™ Puzzlers: Traps, Pitfalls, and Corner Cases

Авторы: Joshua Bloch
Neal Gafter
Издательство: Addison Wesley Professional, 2005
304 страницы

Материал предоставил: Денис Жданов
Найти в магазинах

От издателя

Содержание
Комментарии
Puzzle 89: Generic Drugs

От издателя

How well do you really know Java? Are you a code sleuth? Have you ever spent days chasing a bug caused by a trap or pitfall in Java or its libraries? Do you like brainteasers? Then this is the book for you!

In the tradition of Effective Java™, Bloch and Gafter dive deep into the subtleties of the Java programming language and its core libraries. Illustrated with visually stunning optical illusions, Java™ Puzzlers features 95 diabolical puzzles that educate and entertain. Anyone with a working knowledge of Java will understand the puzzles, but even the most seasoned veteran will find them challenging.

Most of the puzzles take the form of a short program whose behavior isn't what it seems. Can you figure out what it does? Puzzles are grouped loosely according to the features they use, and detailed solutions follow each puzzle. The solutions go well beyond a simple explanation of the program's behavior--they show you how to avoid the underlying traps and pitfalls for good. A handy catalog of traps and pitfalls at the back of the book provides a concise taxonomy for future reference.

Solve these puzzles and you'll never again fall prey to the counterintuitive or obscure behaviors that can fool even the most experienced programmers.

Содержание

Preface

Acknowledgments

Chapter 1. Introduction

Chapter 2. Expressive Puzzlers

Puzzle 1: Oddity
Puzzle 2: Time for a Change
Puzzle 3: Long Division
Puzzle 4: It's Elementary
Puzzle 5: The Joy of Hex
Puzzle 6: Multicast
Puzzle 7: Swap Meat
Puzzle 8: Dos Equis
Puzzle 9: Tweedledum
Puzzle 10: Tweedledee

Chapter 3. Puzzlers with Character

Puzzle 11: The Last Laugh
Puzzle 12: ABC
Puzzle 13: Animal Farm
Puzzle 14: Escape Rout
Puzzle 15: Hello Whirled
Puzzle 16: Line Printer
Puzzle 17: Huh?
Puzzle 18: String Cheese
Puzzle 19: Classy Fire
Puzzle 20: What's My Class?
Puzzle 21: What's My Class, Take 2
Puzzle 22: Dupe of URL
Puzzle 23: No Pain, No Gain

Chapter 4. Loopy Puzzlers

Puzzle 24: A Big Delight in Every Byte
Puzzle 25: Inclement Increment
Puzzle 26: In the Loop
Puzzle 27: Shifty i's
Puzzle 28: Looper
Puzzle 29: Bride of Looper
Puzzle 30: Son of Looper
Puzzle 31: Ghost of Looper
Puzzle 32: Curse of Looper
Puzzle 33: Looper Meets the Wolfman
Puzzle 34: Down for the Count
Puzzle 35: Minute by Minute

Chapter 5. Exceptional Puzzlers

Puzzle 36: Indecision
Puzzle 37: Exceptionally Arcane
Puzzle 38: The Unwelcome Guest
Puzzle 39: Hello, Goodbye
Puzzle 40: The Reluctant Constructor
Puzzle 41: Field and Stream
Puzzle 42: Thrown for a Loop
Puzzle 43: Exceptionally Unsafe
Puzzle 44: Cutting Class
Puzzle 45: Exhausting Workout

Chapter 6. Classy Puzzlers

Puzzle 46: The Case of the Confusing Constructor
Puzzle 47: Well, Dog My Cats!
Puzzle 48: All I Get Is Static
Puzzle 49: Larger Than Life
Puzzle 50: Not Your Type
Puzzle 51: What's the Point?
Puzzle 52: Sum Fun
Puzzle 53: Do Your Thing
Puzzle 54: Null and Void
Puzzle 55: Creationism

Chapter 7. Library Puzzlers

Puzzle 56: Big Problem
Puzzle 57: What's in a Name?
Puzzle 58: Making a Hash of It
Puzzle 59: What's the Difference?
Puzzle 60: One-Liners
Puzzle 61: The Dating Game
Puzzle 62: The Name Game
Puzzle 63: More of the Same
Puzzle 64: The Mod Squad
Puzzle 65: A Strange Saga of a Suspicious Sort

Chapter 8. Classier Puzzlers

Puzzle 66: A Private Matter
Puzzle 67: All Strung Out
Puzzle 68: Shades of Gray
Puzzle 69: Fade to Black
Puzzle 70: Package Deal
Puzzle 71: Import Duty
Puzzle 72: Final Jeopardy
Puzzle 73: Your Privates Are Showing
Puzzle 74: Identity Crisis
Puzzle 75: Heads or Tails?
A Glossary of Name Reuse

Chapter 9. More Library Puzzlers

Puzzle 76: Ping Pong
Puzzle 77: The Lock Mess Monster
Puzzle 78: Reflection Infection
Puzzle 79: It's a Dog's Life
Puzzle 80: Further Reflection
Puzzle 81: Charred Beyond Recognition
Puzzle 82: Beer Blast
Puzzle 83: Dyslexic Monotheism
Puzzle 84: Rudely Interrupted
Puzzle 85: Lazy Initialization

Chapter 10. Advanced Puzzlers

Puzzle 86: Poison-Paren Litter
Puzzle 87: Strained Relations
Puzzle 88: Raw Deal
Puzzle 89: Generic Drugs
Puzzle 90: It's Absurd, It's a Pain, It's Superclass!
Puzzle 91: Serial Killer
Puzzle 92: Twisted Pair
Puzzle 93: Class Warfare
Puzzle 94: Lost in the Shuffle
Puzzle 95: Just Desserts

Appendix A. Catalog of Traps and Pitfalls

1. Lexical Issues
2. Integer Arithmetic
3. Floating-Point Arithmetic
4. Expression Evaluation
5. Flow of Control
6. Class Initialization
7. Instance Creation and Destruction
8. Other Class- and Instance-Related Topics
9. Name Reuse
10. Strings
11. I/O
12. Threads
13. Reflection
14. Serialization
15. Other Libraries

Appendix B. Notes on the Illusions

Ambiguous Figures
Impossible Figures
Geometrical Illusions: Size
Geometrical Illusions: Direction
Subjective Contours
Anomalous Motion Illusions
Illusions of Lightness
Compound Illusions

References
Index

Комментарии

Денис Жданов

На мой взгляд книга стоит того, чтобы прочитать. В ней предлагаются 95 задач, сортированных по разделам, с объяснениями правильных ответов. С частью задач я сталкивался ранее, на часть давал неправильный ответ, больше всего было тех, которые заставляли поломать голову, чтобы найти верное решение. Мне нравится ломать голову над интересными задачками, мне понравилась эта книга. Кроме того, приятно почитать хорошо изложенное объяснение специалиста об известных вещах(problems of silent overflow, mixed computations etc).

Напоследок хочется привести пример паззла, чтобы дать представление о материале книги и о его подаче.

Puzzle 89: Generic Drugs

This program implements a simple linked list data structure. The main program builds a list with two elements and dumps its contents. What does the program print?

public class LinkedList<E> {

    private Node<E> head = null;

    private class Node<E> {

        E value;
        Node<E> next;

        // Node constructor links the node as a new head
        Node(E value) {
            this.value = value;
            this.next = head;
            head = this;
        }
    }

    public void add(E e) {
        new Node<E>(e);
        // Link node as new head
    }

    public void dump() {
        for (Node<E> n = head; n != null; n = n.next)
            System.out.print(n.value + " ");
    }

    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<String>();
        list.add("world");
        list.add("Hello");
        list.dump();
    }
}

Solution 89: Generic Drugs

Again, this program appears reasonably straightforward. New elements are added to the head of the list and the dump method prints the list starting with the head. Therefore, elements are printed in the opposite order they are added. In this case, the program first adds "world" and then "Hello", so it looks as if it is just a convoluted Hello world program. Sadly, if you tried to compile it, you found that it doesn't compile. The error messages from the compiler are downright baffling:

LinkedList.java:11: incompatible types

found : LinkedList<E>.Node<E>

required: LinkedList<E>.Node<E>

            this.next = head;

                        ^

LinkedList.java:12: incompatible types

found : LinkedList<E>.Node<E>

required: LinkedList<E>.Node<E>

            head = this;

                   ^

It appears that the compiler is complaining that a type isn't compatible with itself! Appearances, as usual, are deceiving. The "found" and "required" types are unrelated to each other. They appear identical because the program uses the same name to refer to different types. Specifically, the program contains two different declarations for type parameters named E. The first is the type parameter for LinkedList, and the second is the type parameter for the inner class LinkedList.Node. The latter shadows the former within the inner class. The lesson that we learned in Puzzles 71, 73, and 79 applies here as well: Avoid shadowing type parameter names.

There is no way to refer to a type parameter except by its simple name, so the error message has no way to tell you that these two uses of the name E refer to different types. The error message would be clearer if we systematically renamed the type parameter for Node from E to, say, T. It wouldn't fix the problem, but it would shed some light on it. This approach yields the following error messages:

LinkedList.java:11: inco>mpatible types

found : LinkedList<E>.Node<E>

required: LinkedList<E>.Node<T>

            this.next = head;

                        ^

LinkedList.java:12: incompatible types

found : LinkedList<E>.Node<T>

required: LinkedList<E>.Node<E>

            head = this;

                   ^

What the compiler is trying to tell us is that the program is way too complicated. An inner class of a generic class has access to the type parameters of its outer class. It was the clear intent of the program's author that the type parameter for a Node would always be the same as for the enclosing LinkedList, so there is no reason for Node to have a type parameter of its own. To fix the program, simply eliminate the type parameter in the inner class:

// Fixed but could be MUCH better

public class LinkedList<E> {

    private Node head = null;

    private class Node {

        E value;
        Node next;

        // Node constructor links the node as a new head
        Node(E value) {
            this.value = value;
            this.next = head;
            head = this;
        }
    }
    public void add(E e) {
        new Node(e);
        // Link node as new head
    }

    public void dump() {
        for (Node n = head; n != null; n = n.next)
            System.out.print(n.value + " ");
    }
}

This is the simplest change that fixes the program, but it is not the best. The original program used an inner class unnecessarily. As mentioned in Puzzle 80, you should prefer static member classes over nonstatic [EJ Item 18]. An instance of LinkedList.Node contains not only the value and next fields but also a hidden field containing a reference to the enclosing LinkedList instance.

Although the enclosing instance is used during construction to read and then modify head, it is dead weight once construction has completed. Worse, placing the side effect of changing head into the constructor makes the program confusing to the reader. Change instance fields of a class only in its own instance methods.

A better fix, then, is to modify the original program to move the manipulation of head into LinkedList.add, making Node a static nested class rather than a true inner class. Static nested classes do not have access to the type parameters of enclosing classes, so now Node really does need a type parameter of its own. The resulting program is simple, clear, and correct:

class LinkedList<E> {

    private Node<E> head = null;

    private static class Node<T> {

        T value; 
        Node<T> next;   

        Node(T value, Node<T> next) {
            this.value = value;
            this.next = next;
        }
    }

    public void add(E e) {
        head = new Node<E>(e, head);
    }

    public void dump() {
        for (Node<E> n = head; n != null; n = n.next)
            System.out.print(n.value + " ");
    }
}

In summary, inner classes of generic classes have access to the enclosing class's type parameters, which can be confusing. The misunderstanding illustrated in this puzzle is common among programmers first learning generics. It isn't necessarily wrong to have an inner class in a generic class, but the need for this is rare, and you should consider refactoring your code to avoid it. When you have one generic class nested inside another, give their type parameters different names, even if the nested class is static. For language designers, perhaps it makes sense to forbid shadowing of type parameters, in the same way that shadowing of local variables is forbidden. Such a rule would have caught the bug in this puzzle.

    Сообщений 1    Оценка 34        Оценить