Effective Java Review (90 Part Series)
1 Effective Java Tuesday! Let’s Consider Static Factory Methods
2 Effective Java Tuesday! The Builder Pattern!
… 86 more parts…
3 Effective Java Tuesday! Singletons!
4 Effective Java Tuesday! Utility Classes!
5 Effective Java Tuesday! Prefer Dependency Injection!
6 Effective Java Tuesday! Avoid Creating Unnecessary Objects!
7 Effective Java Tuesday! Don’t Leak Object References!
8 Effective Java Tuesday! Avoid Finalizers and Cleaners!
9 Effective Java Tuesday! Prefer try-with-resources
10 Effective Java Tuesday! Obey the `equals` contract
11 Effective Java Tuesday! Obey the `hashCode` contract
12 Effective Java Tuesday! Override `toString`
13 Effective Java Tuesday! Override `clone` judiciously
14 Effective Java Tuesday! Consider Implementing `Comparable`
15 Effective Java Tuesday! Minimize the Accessibility of Classes and Member
16 Effective Java Tuesday! In Public Classes, Use Accessors, Not Public Fields
17 Effective Java Tuesday! Minimize Mutability
18 Effective Java Tuesday! Favor Composition Over Inheritance
19 Effective Java Tuesday! Design and Document Classes for Inheritance or Else Prohibit It.
20 Effective Java Tuesday! Prefer Interfaces to Abstract Classes
21 Effective Java! Design Interfaces for Posterity
22 Effective Java! Use Interfaces Only to Define Types
23 Effective Java! Prefer Class Hierarchies to Tagged Classes
24 Effective Java! Favor Static Members Classes over Non-Static
25 Effective Java! Limit Source Files to a Single Top-Level Class
26 Effective Java! Don’t Use Raw Types
27 Effective Java! Eliminate Unchecked Warnings
28 Effective Java! Prefer Lists to Array
29 Effective Java! Favor Generic Types
30 Effective Java! Favor Generic Methods
31 Effective Java! Use Bounded Wildcards to Increase API Flexibility
32 Effective Java! Combine Generics and Varargs Judiciously
33 Effective Java! Consider Typesafe Heterogenous Containers
34 Effective Java! Use Enums Instead of int Constants
35 Effective Java! Use Instance Fields Instead of Ordinals
36 Effective Java! Use EnumSet Instead of Bit Fields
37 Effective Java! Use EnumMap instead of Ordinal Indexing
38 Effective Java! Emulate Extensible Enums With Interfaces.
39 Effective Java! Prefer Annotations to Naming Patterns
40 Effective Java! Consistently Use the Override Annotation
41 Effective Java! Use Marker Interfaces to Define Types
42 Effective Java! Prefer Lambdas to Anonymous Classes
43 Effective Java! Prefer Method References to Lambdas
44 Effective Java! Favor the Use of Standard Functional Interfaces
45 Effective Java! Use Stream Judiciously
46 Effective Java! Prefer Side-Effect-Free Functions in Streams
47 Effective Java! Prefer Collection To Stream as a Return Type
48 Effective Java! Use Caution When Making Streams Parallel
49 Effective Java! Check Parameters for Validity
50 Effective Java! Make Defensive Copies When Necessary
51 Effective Java! Design Method Signatures Carefully
52 Effective Java! Use Overloading Judiciously
53 Effective Java! Use Varargs Judiciously
54 Effective Java! Return Empty Collections or Arrays, Not Nulls
55 Effective Java! Return Optionals Judiciously
56 Effective Java: Write Doc Comments For All Exposed APIs
57 Effective Java: Minimize The Scope of Local Variables
58 Effective Java: Prefer for-each loops to traditional for loops
59 Effective Java: Know and Use the Libraries
60 Effective Java: Avoid Float and Double If Exact Answers Are Required
61 Effective Java: Prefer Primitive Types to Boxed Types
62 Effective Java: Avoid Strings When Other Types Are More Appropriate
63 Effective Java: Beware the Performance of String Concatenation
64 Effective Java: Refer to Objects By Their Interfaces
65 Effective Java: Prefer Interfaces To Reflection
66 Effective Java: Use Native Methods Judiciously
67 Effective Java: Optimize Judiciously
68 Effective Java: Adhere to Generally Accepted Naming Conventions
69 Effective Java: Use Exceptions for Only Exceptional Circumstances
70 Effective Java: Use Checked Exceptions for Recoverable Conditions
71 Effective Java: Avoid Unnecessary Use of Checked Exceptions
72 Effective Java: Favor The Use of Standard Exceptions
73 Effective Java: Throw Exceptions Appropriate To The Abstraction
74 Effective Java: Document All Exceptions Thrown By Each Method
75 Effective Java: Include Failure-Capture Information in Detail Messages
76 Effective Java: Strive for Failure Atomicity
77 Effective Java: Don’t Ignore Exceptions
78 Effective Java: Synchronize Access to Shared Mutable Data
79 Effective Java: Avoid Excessive Synchronization
80 Effective Java: Prefer Executors, Tasks, and Streams to Threads
81 Effective Java: Prefer Concurrency Utilities Over wait and notify
82 Effective Java: Document Thread Safety
83 Effective Java: Use Lazy Initialization Judiciously
84 Effective Java: Don’t Depend on the Thread Scheduler
85 Effective Java: Prefer Alternatives To Java Serialization
86 Effective Java: Implement Serializable With Great Caution
87 Effective Java: Consider Using a Custom Serialized Form
88 Effective Java: Write readObject Methods Defensively
89 Effective Java: For Instance Control, Prefer Enum types to readResolve
90 Effective Java: Consider Serialization Proxies Instead of Serialized Instances
This chapter starts a new section of Effective Java about generics. Before Java 5 where generics were introduced to the language, retrieving items from a collection required a cast of the object being returned. Thankfully, in modern Java, we don’t need to deal with this anymore and can use generics to provide us type safety as well as cleaner code.
Let’s briefly go over the definition of a generic in Java. A generic is a class or interface that has one or more type parameters. The way that this ends up looking is the class or interface name followed by angled brackets with the actual type in it. So an example of this would be List<String> myList = ...
. Each generic type also has a raw type
which is simply the generic type without the actual type called out so the raw type
of the previous example would simply be: List myList = ...
. Raw types act as if all the type information is erased and these exist for compatibility reasons. It is the relationship between generic types and raw types that this chapter is about.
Before generics existed in Java if you wanted a collection and add some object to it you would do something like the following:
Collection myStampCollection = ...;
myStampCollection.add(new Stamp(...));
myStampCollection.add(new Coin(...));
(Stamp)myStampCollection.get(1);
Enter fullscreen mode Exit fullscreen mode
Uh oh, looks like I made a mistake. The above code will compile without much issue (other than a vague warning) but at runtime we will get a ClassCastException
. This is rather unfortunate. Whenever we can, we want to push the discovery of all of our issues as early as possible, preferably to compile time. Now let’s look at the above example with generics:
Collection<Stamp> myStampCollection = ...;
myStampCollection.add(new Stamp(...));
// Compile time error
myStampCollection.add(new Coin(...));
myStampCollection.get(1);
Enter fullscreen mode Exit fullscreen mode
With the above where we are telling the compiler the type that will be in the collection it can stop us at compile time and tell us that we are trying to put an incompatible type into the collection. This saves us from having to wait until runtime to discover this issue. Using generics also makes our code cleaner as we don’t have to insert the casts but invisible casts are put into the code for us.
So why do raw types exist? Well to it’s benefit and detriment Java has always held backwards compatibility as a top requirement and thus in order to allow generic types to coexist with raw types, at compile time, the types are removed from the generics, this is called type erasure.
While this is helpful for backward compatibility it does bring with it it’s own issues that we will discuss in a future chapter. As you use generics it is helpful to know that their benefit is largely pre and during compile time only, they disappear after being compiled. (We will discuss this further in a future post but it can help you when you try to do certain things and the compiler throws errors saying it’s not possible.) It comes down to when we use raw types we forfeit all benefits that the generics give us as far as safety and expressiveness.
So what if we have a type that doesn’t have a specific actual type it’s related to, is that an acceptable time to use a raw type? Even then using List<Object>
over simply List
is prefered. The reasoning being that the raw type opts out of the generic type system. For example while we can pass a List<String>
to a method that takes a List
we cannot pass that collection to a method that takes a List<Object>
What about cases where we don’t care about the type, you may be tempted in this case to use a raw type. Again, if we use a raw type we forfeit the safety of the generic type system. In this case we can use unbounded wildcard types.
These take the form of replacing the actual type with a ?
. So for example if we wanted to create a method that counts the number of elements in common between two sets we could create signature as follows:
static int elementsInCommon(Set<?> set1, Set<?> set2) { ... }
Enter fullscreen mode Exit fullscreen mode
Since this method likely only relies on an equals
method which is common to all objects this is a fine signature. When using an unbounded wildcard type the compiler will prevent us from inserting anything other than null
this gives us further safety. Since we all need to do is read the objects as opaque values this turns out fine for us.
So is there anywhere where using the raw type is acceptable? There are a few exceptions where it is useful/required
- Class literals cannot use generics.
List.class
is legal,List<String>.class
is not. - In
instanceof
checks. Because of our friend type erasure the type information is removed at runtime and thus the only legal parameterized type for an instance of check is the unbounded wildcard which doesn’t provide us any value in aninstanceof
check and thus just adds noise. So doing something likemyObject instanceof List
is the preferred method overmyObject instanceof List<?>
. However once we check the type we should cast to an unbounded wildcard type for additional safety.
if (myObject instanceof List) {
List<?> myList = (List<?>) myObject;
...
}
Enter fullscreen mode Exit fullscreen mode
So in summary, raw types continue to exist in Java for compatibility reasons with code that predates generics and can lead to errors at runtime. Because of this, raw types should not be used and generic types should be used instead to lead to safer, cleaner code.
Effective Java Review (90 Part Series)
1 Effective Java Tuesday! Let’s Consider Static Factory Methods
2 Effective Java Tuesday! The Builder Pattern!
… 86 more parts…
3 Effective Java Tuesday! Singletons!
4 Effective Java Tuesday! Utility Classes!
5 Effective Java Tuesday! Prefer Dependency Injection!
6 Effective Java Tuesday! Avoid Creating Unnecessary Objects!
7 Effective Java Tuesday! Don’t Leak Object References!
8 Effective Java Tuesday! Avoid Finalizers and Cleaners!
9 Effective Java Tuesday! Prefer try-with-resources
10 Effective Java Tuesday! Obey the `equals` contract
11 Effective Java Tuesday! Obey the `hashCode` contract
12 Effective Java Tuesday! Override `toString`
13 Effective Java Tuesday! Override `clone` judiciously
14 Effective Java Tuesday! Consider Implementing `Comparable`
15 Effective Java Tuesday! Minimize the Accessibility of Classes and Member
16 Effective Java Tuesday! In Public Classes, Use Accessors, Not Public Fields
17 Effective Java Tuesday! Minimize Mutability
18 Effective Java Tuesday! Favor Composition Over Inheritance
19 Effective Java Tuesday! Design and Document Classes for Inheritance or Else Prohibit It.
20 Effective Java Tuesday! Prefer Interfaces to Abstract Classes
21 Effective Java! Design Interfaces for Posterity
22 Effective Java! Use Interfaces Only to Define Types
23 Effective Java! Prefer Class Hierarchies to Tagged Classes
24 Effective Java! Favor Static Members Classes over Non-Static
25 Effective Java! Limit Source Files to a Single Top-Level Class
26 Effective Java! Don’t Use Raw Types
27 Effective Java! Eliminate Unchecked Warnings
28 Effective Java! Prefer Lists to Array
29 Effective Java! Favor Generic Types
30 Effective Java! Favor Generic Methods
31 Effective Java! Use Bounded Wildcards to Increase API Flexibility
32 Effective Java! Combine Generics and Varargs Judiciously
33 Effective Java! Consider Typesafe Heterogenous Containers
34 Effective Java! Use Enums Instead of int Constants
35 Effective Java! Use Instance Fields Instead of Ordinals
36 Effective Java! Use EnumSet Instead of Bit Fields
37 Effective Java! Use EnumMap instead of Ordinal Indexing
38 Effective Java! Emulate Extensible Enums With Interfaces.
39 Effective Java! Prefer Annotations to Naming Patterns
40 Effective Java! Consistently Use the Override Annotation
41 Effective Java! Use Marker Interfaces to Define Types
42 Effective Java! Prefer Lambdas to Anonymous Classes
43 Effective Java! Prefer Method References to Lambdas
44 Effective Java! Favor the Use of Standard Functional Interfaces
45 Effective Java! Use Stream Judiciously
46 Effective Java! Prefer Side-Effect-Free Functions in Streams
47 Effective Java! Prefer Collection To Stream as a Return Type
48 Effective Java! Use Caution When Making Streams Parallel
49 Effective Java! Check Parameters for Validity
50 Effective Java! Make Defensive Copies When Necessary
51 Effective Java! Design Method Signatures Carefully
52 Effective Java! Use Overloading Judiciously
53 Effective Java! Use Varargs Judiciously
54 Effective Java! Return Empty Collections or Arrays, Not Nulls
55 Effective Java! Return Optionals Judiciously
56 Effective Java: Write Doc Comments For All Exposed APIs
57 Effective Java: Minimize The Scope of Local Variables
58 Effective Java: Prefer for-each loops to traditional for loops
59 Effective Java: Know and Use the Libraries
60 Effective Java: Avoid Float and Double If Exact Answers Are Required
61 Effective Java: Prefer Primitive Types to Boxed Types
62 Effective Java: Avoid Strings When Other Types Are More Appropriate
63 Effective Java: Beware the Performance of String Concatenation
64 Effective Java: Refer to Objects By Their Interfaces
65 Effective Java: Prefer Interfaces To Reflection
66 Effective Java: Use Native Methods Judiciously
67 Effective Java: Optimize Judiciously
68 Effective Java: Adhere to Generally Accepted Naming Conventions
69 Effective Java: Use Exceptions for Only Exceptional Circumstances
70 Effective Java: Use Checked Exceptions for Recoverable Conditions
71 Effective Java: Avoid Unnecessary Use of Checked Exceptions
72 Effective Java: Favor The Use of Standard Exceptions
73 Effective Java: Throw Exceptions Appropriate To The Abstraction
74 Effective Java: Document All Exceptions Thrown By Each Method
75 Effective Java: Include Failure-Capture Information in Detail Messages
76 Effective Java: Strive for Failure Atomicity
77 Effective Java: Don’t Ignore Exceptions
78 Effective Java: Synchronize Access to Shared Mutable Data
79 Effective Java: Avoid Excessive Synchronization
80 Effective Java: Prefer Executors, Tasks, and Streams to Threads
81 Effective Java: Prefer Concurrency Utilities Over wait and notify
82 Effective Java: Document Thread Safety
83 Effective Java: Use Lazy Initialization Judiciously
84 Effective Java: Don’t Depend on the Thread Scheduler
85 Effective Java: Prefer Alternatives To Java Serialization
86 Effective Java: Implement Serializable With Great Caution
87 Effective Java: Consider Using a Custom Serialized Form
88 Effective Java: Write readObject Methods Defensively
89 Effective Java: For Instance Control, Prefer Enum types to readResolve
90 Effective Java: Consider Serialization Proxies Instead of Serialized Instances
暂无评论内容