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
The most common use cases of generics are collections such as List
and Map
as well as single-element containers like ThreadLocal
and AtomicReference
. In both the collection case as well as the single-element objects there is a very finite list of types taken. This serves us well in many cases however sometimes we need additional flexibility. We may have a use case like mapping a row from a database in a type safe manner. In these cases we have to use another technique. This technique is to parameterize the key to the data and not the container entirely. That is, rather than specifying the type of the container we specify the type of a particular piece of data. By doing this we can enable additional flexibility while still preserving type safety.
Let’s consider an example. Consider a Favorite
class. The purpose of this class is to collect your favorite object of an arbitrary amount of types. The Class
object will serve as the key into our Favorite
class. This works because the Class
type is generic and holds it’s own type Class<T>
. Thus String.class
is Class<String>
, Integer.class
is Class<Integer>
, and so on.
The API for our Favorite
class is straightforward:
public class Favorites {
public <T> void putFavorite(Class<T> type, T value);
public <T> T getFavorite(Class<T> type);
}
Enter fullscreen mode Exit fullscreen mode
and its use would look something like:
Favorites favorites = new Favorites();
favorites.putFavorite(String.class, "Hello world");
favorites.putFavorite(Integer.class, 123);
favorites.putFavorite(Class.class, Favorite.class);
Enter fullscreen mode Exit fullscreen mode
and finally let’s check out the implementation:
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type, T value) {
favorites.put(Objects.requireNonNull(type), value);
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
}
Enter fullscreen mode Exit fullscreen mode
For what this class does this is not too complicated. What the Favorite
class ends up representing is a type-safe as well as has a different type for each element, thus heterogeneous. This is how we get to a heterogeneous container description from the chapter title. Looking at the above implementation probably the most interesting part is the getFavorite
function. Because we lose the type information once we put the value in the Map
we need a way to retrieve the value in a type safe manner. This is where the Class.cast
function comes in handy. If the object can’t be cast to the type we will get a ClassCastException
. Because we control the types that go into the Map
we know this won’t happen in the usual case.
What limitations does our Favorites
implementation have? The first one is if a user of our class creates a raw class object they can corrupt the type safety of our class. This is no different than having a HashSet<Integer>
and putting a String
by accessing it as a raw HashSet
. We actually can enforce runtime type safety if we are willing to pay for it. By changing the putFavorite
function to the following we can enforce the type safety at runtime even if accessed via raw types.
public <T> void putFavorite(Class<T> type, T value) {
favorites.put(Objects.requireNonNull(type), type.cast(value));
}
Enter fullscreen mode Exit fullscreen mode
There are collection wrappers in java.util.Collections
that do this same thing. checkedList
, checkedSet
, etc perform the same runtime check and can be useful when tracking down where type unsafe changes are made in code that works with parameterized as well as raw collections.
The second limitation is that our Favorites
class can’t be used with non-reifiable types. This means that we can store our favorite String
, Integer
, String[]
but we cannot store our favorite List<String>
and other non-reifiable types. This is because at runtime we lose the parameterized part of our type and are just left with List.class
in the above cause and thus there would be no way of knowing the difference between List<String>
and List<Integer>
at runtime.
While often we can get by with parameterized collections and single element types sometimes we want the extra flexibility of typesafe heterogeneous containers. This is made possible via the Class
type that allows type safety in a more dynamic manner.
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
原文链接:Effective Java! Consider Typesafe Heterogenous Containers
暂无评论内容