Effective Java
Rate it:
Open Preview
Read between August 14, 2018 - April 3, 2019
36%
Flag icon
The answer is that methods with varargs parameters of generic or parameterized types can be very useful in practice, so the language designers opted to live with this inconsistency. In fact, the Java libraries export several such methods, including Arrays.asList(T... a), Collections.addAll(Collection<? super T> c, T... elements), and EnumSet.of(E first, E... rest). Unlike the dangerous method shown earlier, these library methods are typesafe.
Joe Soltzberg
This is very similar to what I wanted to do....
37%
Flag icon
In essence, the SafeVarargs annotation constitutes a promise by the author of a method that it is typesafe. In exchange for this promise, the compiler agrees not to warn the users of the method that calls may be unsafe. It is critical that you do not annotate a method with @SafeVarargs unless it actually is safe.
Joe Soltzberg
Sounds risky to leave this to human error. Part of the whole benefit of a type system is not having to worry about human error
37%
Flag icon
Recall that a generic array is created when the method is invoked, to hold the varargs parameters. If the method doesn’t store anything into the array (which would overwrite the parameters) and doesn’t allow a reference to the array to escape (which would enable untrusted code to access the array), then it’s safe. In other words, if the varargs parameter array is used only to transmit a variable number of arguments from the caller to the method—which is, after all, the purpose of varargs—then the method is safe.
37%
Flag icon
it is unsafe to give another method access to a generic varargs parameter array, with two exceptions: it is safe to pass the array to another varargs method that is correctly annotated with @SafeVarargs, and it is safe to pass the array to a non-varargs method that merely computes some function of the contents of the array.
37%
Flag icon
As a simple example of this approach, consider a Favorites class that allows its clients to store and retrieve a favorite instance of arbitrarily many types. The Class object for the type will play the part of the parameterized key. The reason this works is that class Class is generic. The type of a class literal is not simply Class, but Class<T>. For example, String.class is of type Class<String>, and Integer.class is of type Class<Integer>. When a class literal is passed among methods to communicate both compile-time and runtime type information, it is called a type token [Bracha04].
38%
Flag icon
The second limitation of the Favorites class is that it cannot be used on a non-reifiable type (Item 28). In other words, you can store your favorite String or String[], but not your favorite List<String>. If you try to store your favorite List<String>, your program won’t compile. The reason is that you can’t get a Class object for List<String>.
38%
Flag icon
Favorites object if the “type literals” List<String>.class and List<Integer>.class were legal and returned the same object reference. There is no entirely satisfactory workaround for this limitation.
Joe Soltzberg
So then what would you do if you wanted a kind of Favorites class where there could be lists? Create a wrapper class?
39%
Flag icon
Sometimes you may need to limit the types that can be passed to a method. This can be achieved with a bounded type token, which is simply a type token that places a bound on what type can be represented, using a bounded type parameter (Item 30) or a bounded wildcard (Item 31).
40%
Flag icon
Until 2006, two years after enums were added to Java, Pluto was a planet. This raises the question “what happens when you remove an element from an enum type?” The answer is that any client program that doesn’t refer to the removed element will continue to work fine. So, for example, our WeightTable program would simply print a table with one fewer row. And what of a client program that refers to the removed element (in this case, Planet.Pluto)? If you recompile the client program, the compilation will fail with a helpful error message at the line that refers to the erstwhile planet; if you ...more
41%
Flag icon
What you really want is to be forced to choose an overtime pay strategy each time you add an enum constant. Luckily, there is a nice way to achieve this. The idea is to move the overtime pay computation into a private nested enum, and to pass an instance of this strategy enum to the constructor for the PayrollDay enum. The PayrollDay enum then delegates the overtime pay calculation to the strategy enum, eliminating the need for a switch statement or constant-specific method implementation in PayrollDay. While this pattern is less concise than the switch statement, it is safer and more ...more
Joe Soltzberg
Awesome
41%
Flag icon
Enums are, generally speaking, comparable in performance to int constants. A minor performance disadvantage of enums is that there is a space and time cost to load and initialize enum types, but it is unlikely to be noticeable in practice.
Joe Soltzberg
Not really on Android in the past -- but almost never enough to justify not using them IMO (unless it was at a framework level)
42%
Flag icon
EnumSet instead of bit fields
Joe Soltzberg
A better abstraction
42%
Flag icon
The reason that EnumMap is comparable in speed to an ordinal-indexed array is that EnumMap uses such an array internally, but it hides this implementation detail from the programmer, combining the richness and type safety of a Map with the speed of an array. Note that the EnumMap constructor takes the Class object of the key type: this is a bounded type token, which provides runtime generic type information (Item 33).
43%
Flag icon
 private static final Map<Phase, Map<Phase, Transition>>         m = Stream.of(values()).collect(groupingBy(t -> t.from,          () -> new EnumMap<>(Phase.class),          toMap(t -> t.to, t -> t,             (x, y) -> y, () -> new EnumMap<>(Phase.class))));
Joe Soltzberg
Code like this needs to be broken up IMO for readability
43%
Flag icon
That said, there is at least one compelling use case for extensible enumerated types, which is operation codes, also known as opcodes.
Joe Soltzberg
I've ran into this problem before when making a Java wrapper for a CLI tool
44%
Flag icon
A minor disadvantage of the use of interfaces to emulate extensible enums is that implementations cannot be inherited from one enum type to another. If the implementation code does not rely on any state, it can be placed in the interface, using default implementations (Item 20). In the case of our Operation example, the logic to store and retrieve the symbol associated with an operation must be duplicated in BasicOperation and ExtendedOperation. In this case it doesn’t matter because very little code is duplicated. If there were a larger amount of shared functionality, you could encapsulate it ...more
Joe Soltzberg
That's why I wish it was easier to share base/abstract functionality between enums in situations like this
44%
Flag icon
The comment before the Test annotation declaration says, “Use only on parameterless static methods.” It would be nice if the compiler could enforce this, but it can’t, unless you write an annotation processor to do so. For more on this topic, see the documentation for javax.annotation.processing. In the absence of such an annotation processor, if you put a Test annotation on the declaration of an instance method or on a method with one or more parameters, the test program will still compile, leaving it to the testing tool to deal with the problem at runtime.
45%
Flag icon
As of Java 8, there is another way to do multivalued annotations. Instead of declaring an annotation type with an array parameter, you can annotate the declaration of an annotation with the @Repeatable meta-annotation, to indicate that the annotation may be applied repeatedly to a single element. This meta-annotation takes a single parameter, which is the class object of a containing annotation type, whose sole parameter is an array of the annotation type [JLS, 9.6.3]. Here’s how the annotation declarations look if we take this approach with our ExceptionTest annotation. Note that the ...more
46%
Flag icon
If an element has a repeated annotation of some type and you use the isAnnotationPresent method to check if the element has an annotation of that type, you’ll find that it does not.
Joe Soltzberg
:/
46%
Flag icon
Therefore, you should use the Override annotation on every method declaration that you believe to override a superclass declaration.
Joe Soltzberg
This was one of the first notable programming bugs I ran into an spent hours on. I didn't use @Override for the 'paint' method in some Java swing code and spent a long time wondering why my paint method wasn't being used...
48%
Flag icon
Unlike methods and classes, lambdas lack names and documentation; if a computation isn’t self-explanatory, or exceeds a few lines, don’t put it in a lambda.
Joe Soltzberg
One of the biggest issues I see in functional programming. If the lambda is not a trivial operation then it deserves its own method. Just because most functional examples use lambdas as part of map(), fold(), etc doesn't mean you should if it's not immediately obvious what the lambda is doing
48%
Flag icon
If you’re programming with an IDE, it will offer to replace a lambda with a method reference wherever it can. You should usually, but not always, take the IDE up on the offer. Occasionally, a lambda will be more succinct than a method reference. This happens most often when the method is in the same class as the lambda. For example, consider this snippet, which is presumed to occur in a class named GoshThisClassNameIsHumongous:
49%
Flag icon
Most of the standard functional interfaces exist only to provide support for primitive types. Don’t be tempted to use basic functional interfaces with boxed primitives instead of primitive functional interfaces. While it works, it violates the advice of Item 61, “prefer primitive types to boxed primitives.” The performance consequences of using boxed primitives for bulk operations can be deadly.
49%
Flag icon
You should seriously consider writing a purpose-built functional interface in preference to using a standard one if you need a functional interface that shares one or more of the following characteristics with Comparator: • It will be commonly used and could benefit from a descriptive name. • It has a strong contract associated with it. • It would benefit from custom default methods.
50%
Flag icon
A stream pipeline consists of a source stream followed by zero or more intermediate operations and one terminal operation. Each intermediate operation transforms the stream in some way, such as mapping each element to a function of that element or filtering out all elements that do not satisfy some condition. Intermediate operations all transform one stream into another, whose element type may be the same as the input stream or different from it. The terminal operation performs a final computation on the stream resulting from the last intermediate operation, such as storing its elements into a ...more
50%
Flag icon
 try (Stream<String> words = Files.lines(dictionary)) {          words.collect(groupingBy(word -> alphabetize(word)))            .values().stream()            .filter(group -> group.size() >= minGroupSize)            .forEach(g -> System.out.println(g.size() + ": " + g));       }
Joe Soltzberg
This is how it should be done
50%
Flag icon
In the absence of explicit types, careful naming of lambda parameters is essential to the readability of stream pipelines. Note also that word alphabetization is done in a separate alphabetize method. This enhances readability by providing a name for the operation and keeping implementation details out of the main program. Using helper methods is even more important for readability in stream pipelines than in iterative code because pipelines lack explicit type information and named temporary variables.
51%
Flag icon
The forEach operation should be used only to report the result of a stream computation, not to perform the computation. Occasionally, it makes sense to use forEach for some other purpose, such as adding the results of a stream computation to a preexisting collection.
Joe Soltzberg
If you struggle to build a functional stream for a given problem and are having to resort to a forEach, then it's usually either: 1. Not enough functional skills to tackle the problem 2. It could be done more cleanly using an iterative style
52%
Flag icon
. It is customary and wise to statically import all members of Collectors because it makes stream pipelines more readable.
52%
Flag icon
If you’re using an earlier release, you can translate the scanner, which implements Iterator, into a stream using an adapter similar to the one in Item 47 (streamOf(Iterable<E>)).
52%
Flag icon
Loosely speaking, it says, “convert the stream of albums to a map, mapping each artist to the album that has the best album by sales.” This is surprisingly close to the problem statement.
Joe Soltzberg
Much better than an imperative method where one would have to deduce that sentence from inspecting the code. With a more functional method using streams (as done here) the code and the actual problem statement are almost the same and it reads nicely (it's also easier to develop and less prone to bugs IMO)
53%
Flag icon
The norm was a collection interface. If the method existed solely to enable for-each loops or the returned sequence couldn’t be made to implement some Collection method (typically, contains(Object)), the Iterable interface was used.
Joe Soltzberg
Or a collection that can handle a large (or infinite) number of values and not report the max size as being INT.MAX_VALUE
53%
Flag icon
Integer.MAX_VALUE, or 231 − 1. The Collection specification does allow the size method to return 231 − 1 if the collection is larger, even infinite, but this is not a wholly satisfying solution.
Joe Soltzberg
Why was this done in the first place?
54%
Flag icon
The moral of this story is simple: Do not parallelize stream pipelines indiscriminately. The performance consequences may be disastrous.
54%
Flag icon
What these data structures have in common is that they can all be accurately and cheaply split into subranges of any desired sizes, which makes it easy to divide work among parallel threads. The abstraction used by the streams library to perform this task is the spliterator, which is returned by the spliterator method on Stream and Iterable.
54%
Flag icon
Another important factor that all of these data structures have in common is that they provide good-to-excellent locality of reference when processed sequentially: sequential element references are stored together in memory.
55%
Flag icon
As a very rough estimate, the number of elements in the stream times the number of lines of code executed per element should be at least a hundred thousand [Lea14].
Joe Soltzberg
Interesting...
55%
Flag icon
For example, it is not uncommon that index values must be non-negative and object references must be non-null. You should clearly document all such restrictions and enforce them with checks at the beginning of the method body. This is a special case of the general principle that you should attempt to detect errors as soon as possible after they occur. Failing to do so makes it less likely that an error will be detected and makes it harder to determine the source of an error once it has been detected.
Joe Soltzberg
I wish there was better compile-time, language-level support for this
55%
Flag icon
For an unexported method, you, as the package author, control the circumstances under which the method is called, so you can and should ensure that only valid parameter values are ever passed in. Therefore, nonpublic methods can check their parameters using assertions, as shown below: Click here to view code image // Private helper function for a recursive sort private static void sort(long a[], int offset, int length) {     assert a != null;     assert offset >= 0 && offset <= a.length;     assert length >= 0 && length <= a.length - offset;     ... // Do the computation }
Joe Soltzberg
What is the point of this feature? Wouldn't it make sense to just implement yourself in a utils class that can be abstracted further if/when needed?
56%
Flag icon
With the new constructor in place, the previous attack will have no effect on the Period instance. Note that defensive copies are made before checking the validity of the parameters (Item 49), and the validity check is performed on the copies rather than on the originals. While this may seem unnatural, it is necessary. It protects the class against changes to the parameters from another thread during the window of vulnerability between the time the parameters are checked and the time they are copied. In the computer security community, this is known as a time-of-check/time-of-use or TOCTOU ...more
Joe Soltzberg
Also the validity check could have side-effects itself
57%
Flag icon
Prefer two-element enum types to boolean parameters,
Joe Soltzberg
Agreed - especially for readability
58%
Flag icon
A safe, conservative policy is never to export two overloadings with the same number of parameters. If a method uses varargs, a conservative policy is not to overload it at all, except as described in Item 53. If you adhere to these restrictions, programmers will never be in doubt as to which overloading applies to any set of actual parameters. These restrictions are not terribly onerous because you can always give methods different names instead of overloading them.
60%
Flag icon
Note that we pass in an exception factory rather than an actual exception. This avoids the expense of creating the exception unless it will actually be thrown: Click here to view code image // Using an optional to throw a chosen exception Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);
Joe Soltzberg
Cool
60%
Flag icon
When programming with streams, it is not uncommon to find yourself with a Stream<Optional<T>> and to require a Stream<T> containing all the elements in the nonempty optionals in order to proceed. If you’re using Java 8, here’s how to bridge the gap: Click here to view code image streamOfOptionals     .filter(Optional::isPresent)     .map(Optional::get)
61%
Flag icon
As a rule, you should declare a method to return Optional<T> if it might not be able to return a result and clients will have to perform special processing if no result is returned.
61%
Flag icon
Therefore, you should never return an optional of a boxed primitive type,
61%
Flag icon
More generally, it is almost never appropriate to use an optional as a key, value, or element in a collection or array.
61%
Flag icon
To document your API properly, you must precede every exported class, interface, constructor, method, and field declaration with a doc comment.
Joe Soltzberg
I tend to agree with this, but what about something simple like removeLast() for a list or plus(int a, int b) that adds two numbers?
61%
Flag icon
With the exception of methods in classes designed for inheritance (Item 19), the contract should say what the method
61%
Flag icon
The doc comment should enumerate all of the method’s preconditions, which are the things that have to be true in order for a client to invoke it, and its postconditions, which are the things that will be true after the invocation has completed successfully. Typically, preconditions are described implicitly by the @throws tags for unchecked exceptions; each unchecked exception corresponds to a precondition violation. Also, preconditions can be specified along with the affected parameters in their @param tags.
Joe Soltzberg
I wish there was just an @precondition and @postcondition